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

How does adding some text make this regex match the input even though there's no lookahead?

在回答 时,我想到了这个正则表达式:

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

(注意:此正则表达式需要 PyPI regex module

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

此正则表达式可正确处理问题中给出的 2 个示例:

>>> pattern = r'(?:(?!)(?:,foo=([^,]*),(?=())|.))*bar=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')
>>>

但是如果 foo= after a bar=2:

会发生一些奇怪的事情
>>> # 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,
  2. 处找到foo=
  3. 正则表达式尝试匹配 bar=2,但失败了
  4. 正则表达式一次回溯 1 个字符,直到 bar=2 匹配,得到 notfoo=1,bar=2.
  5. 的结果

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

(?:(?!)(?:,foo=([^,]*),(?=())bar=2()|.))*
>>> pattern = r'(?:(?!)(?:,foo=([^,]*),(?=())bar=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')
>>> regex.match(pattern, 'notfoo=1,bar=2')
>>> regex.match(pattern, 'notfoo=1,bar=2,foo=3,')
>>>