正则表达式末尾加斜杠为何匹配结果出现差异?
正则末尾加斜杠与不加匹配结果不同的原因
核心是末尾斜杠起到了锚定匹配终点的作用,再结合正则的贪婪匹配特性、分支匹配顺序,以及re.match仅匹配字符串开头的规则,最终导致两种情况的匹配逻辑出现差异:
带斜杠的正则匹配逻辑
正则代码:
re.match('data/(?P<yyyy>[12]\d{3})(?P<mm>0?[1-9]|1[0-2])(?P<dd>3[0-1]|[1-2][0-9]|0?[1-9])/', 'data/20221231/file01.csv').groupdict()
末尾的/强制正则必须匹配到目标字符串中20221231后面的那个/,这就要求各个捕获组必须严格匹配合法日期格式:
yyyy匹配4位年份:2022mm必须匹配符合规则的月份,只能选择2位的12(如果只匹配1位的1,剩下的231无法让dd匹配后刚好到/)dd匹配31,刚好触碰到末尾的/,完全符合正则规则,因此得到正确结果:{'dd': '31', 'mm': '12', 'yyyy': '2022'}
不带斜杠的正则匹配逻辑
正则代码:
re.match('data/(?P<yyyy>[12]\d{3})(?P<mm>0?[1-9]|1[0-2])(?P<dd>3[0-1]|[1-2][0-9]|0?[1-9])', 'data/20221231/file01.csv').groupdict()
没有末尾的/限制时,re.match只要求从字符串开头匹配成功即可,后续内容不影响匹配结果。此时正则的分支匹配顺序(先尝试短分支)和贪婪特性会导致非预期匹配:
yyyy仍然匹配2022mm的分支顺序是0?[1-9]|1[0-2],正则会先尝试匹配短分支0?[1-9],这里可以匹配2022后的第一个字符1- 剩下的
231交给dd,dd的分支[1-2][0-9]可以匹配23,此时整个正则已经匹配了data/2022123,满足re.match的开头匹配要求,因此得到错误结果:{'dd': '23', 'mm': '1', 'yyyy': '2022'}
解决办法
- 保留末尾斜杠:这是最直接的方式,用斜杠锚定日期的结束位置,确保匹配到完整的合法日期
- 使用正向预查替代斜杠:如果不想消耗斜杠,可以用
(?=/)代替末尾的/,正则变为:re.match('data/(?P<yyyy>[12]\d{3})(?P<mm>0?[1-9]|1[0-2])(?P<dd>3[0-1]|[1-2][0-9]|0?[1-9])(?=/)', 'data/20221231/file01.csv').groupdict() - 调整分支顺序:把更长的合法分支放在前面,比如
mm的正则改为1[0-2]|0?[1-9],让正则优先尝试匹配2位的月份,不过这种方法不如锚定可靠,若日期后没有分隔符仍可能出错
内容的提问来源于stack exchange,提问作者Oleksandr Baranov
相关产品推荐
相关产品推荐

