添加一些文本如何使此正则表达式与输入匹配,即使没有前瞻?



在回答这个问题时,我提出了以下正则表达式:

(?:(?!2)(?:,foo=([^,]*),(?=())|.))*2bar=2

(注意:此正则表达式需要PyPIregex模块(

(简短解释:正则表达式依赖于这样一个事实,即lookahead中的捕获组在匹配一次后无法更改其值,因此在找到第一个foo=后,(?=())匹配,从那时起(?!2)将始终失败。(

此正则表达式与问题中给出的2个示例正确配合使用:

>>> pattern = r'(?:(?!2)(?:,foo=([^,]*),(?=())|.))*2bar=2'
>>> regex.match(pattern, 'baz=0,foo=1,bar=2,foo=3,bar=4').group(1)
'1'
>>> regex.match(pattern, 'baz=0,foo=1,foo=1,bar=2')
>>>

但是,如果在bar=2:之后出现foo=,就会发生一些奇怪的事情

>>> # this doesn't match, as expected:
>>> regex.match(pattern, 'notfoo=1,bar=2')
>>> # but how the heck does it match this ?!
>>> regex.match(pattern, 'notfoo=1,bar=2,foo=3,')
<regex.Match object; span=(0, 14), match='notfoo=1,bar=2'>

正如您所看到的,字符串'notfoo=1,bar=2,foo=3,'产生了一个notfoo=1,bar=2的匹配。foo=3,甚至不包括在匹配中,但如果删除它,正则表达式将不再匹配!这怎么可能?这是regex模块中的错误吗?

这实际上很有道理。这种行为的原因很简单:回溯。

事件顺序如下:

  1. 贪婪组(?:...)*一次前进一个字符,直到它最终在,foo=3,处发现foo=的出现
  2. 正则表达式尝试匹配bar=2,但失败
  3. 正则表达式每次回溯1个字符,直到bar=2匹配,得到notfoo=1,bar=2的结果

那么,我们该怎么办呢?我们可以将bar=2移动到贪婪组中,并使用另一个捕获组来断言正则表达式匹配成功:

(?:(?!2)(?:,foo=([^,]*),(?=())bar=2()|.))*3
>>> pattern = r'(?:(?!2)(?:,foo=([^,]*),(?=())bar=2()|.))*3'
>>> regex.match(pattern, 'baz=0,foo=1,bar=2,foo=3,bar=4').group(1)
'1'
>>> regex.match(pattern, 'baz=0,foo=1,foo=1,bar=2')
>>> regex.match(pattern, 'notfoo=1,bar=2')
>>> regex.match(pattern, 'notfoo=1,bar=2,foo=3,')
>>>

最新更新