如何编写与关键词顺序无关的正则表达式提取/send命令参数
问题:匹配/send命令中任意顺序的参数分组
需求是对/send命令后的参数进行分组,关键词包括at、after、until(对应时间参数)和for(对应时长参数),同时提取第一个数字作为postNumber,且参数顺序不固定。具体输入输出示例如下:
- 输入:
/send 1 at 11:00pm for 3min→ 期望结果:postNumber = 1, sendAt = 11:00pm, duration = 3min - 输入:
/send 1 for 3min→ 期望结果:postNumber = 1, duration = 3min - 输入:
/send 1 at 11:00pm→ 期望结果:postNumber = 1, sendAt = 11:00pm - 输入:
/send 1 until 11:00pm→ 期望结果:postNumber = 1, until = 11:00pm - 输入:
/send 1 for 3min at 11:00 pm→ 期望结果:postNumber = 1, sendAt = 11:00 pm, duration = 3min - 输入:
/send 1 at 11am for 1 h→ 期望结果:postNumber = 1, sendAt = 11am, duration = 1 h
之前尝试的正则无法支持参数顺序无关的匹配:
(?<postNumber>\d+)(?: at (?<sendAt>.*))?(?: for (?<duration>.*))?(?: until (?<until>.*))?
使用正向预查的版本也存在问题,导致第一个组匹配异常:
(?<postNumber>\d+)(?=(?: at (?<timeToSend>.*)))?(?=(?: for (?<duration>.*)))?(?=(?: until (?<until>.*)))?
解决方案
要实现任意顺序的参数匹配,核心是独立匹配每个参数组,不依赖顺序,同时确保每个关键词对应的内容不会包含其他关键词(避免贪婪匹配导致的错误)。
直接生成对应命名组的正则
以下正则适配PCRE/JavaScript等支持命名捕获组的引擎,可直接捕获到postNumber、sendAt、after、until、duration这些目标分组:
^\/send (?<postNumber>\d+)(?:\s+(?:at\s+(?<sendAt>(?:(?!\s+(?:at|after|until|for)\b).*))|after\s+(?<after>(?:(?!\s+(?:at|after|until|for)\b).*))|until\s+(?<until>(?:(?!\s+(?:at|after|until|for)\b).*))|for\s+(?<duration>(?:(?!\s+(?:at|after|until|for)\b).*)))*$
正则逻辑解释
^\/send:精准匹配命令开头的/send前缀(?<postNumber>\d+):捕获第一个连续数字作为postNumber(?:\s+(?:...|...))*:匹配任意数量的参数组(顺序不限),每组前需有空格分隔- 针对
at/after/until:用(?!\s+(?:at|after|until|for)\b)负向预查限制捕获内容,确保时间参数不会包含其他关键词,避免贪婪匹配吞掉后续参数 - 针对
for:同样用负向预查限制时长参数的范围,确保不会覆盖其他参数
- 针对
原正则失效原因
- 第一版正则是按固定顺序匹配的,后续分组必须在前面分组之后才能被捕获,无法处理参数顺序调换的场景
- 正向预查版本中,预查不消耗字符,且
.*贪婪匹配会吞掉所有后续内容,导致捕获逻辑完全失效
内容的提问来源于stack exchange,提问作者user13345587
相关产品推荐
相关产品推荐

