在回答这个问题时,我提出了以下正则表达式:
(?:(?!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
模块中的错误吗?
这实际上很有道理。这种行为的原因很简单:回溯。
事件顺序如下:
- 贪婪组
(?:...)*
一次前进一个字符,直到它最终在,foo=3,
处发现foo=
的出现 - 正则表达式尝试匹配
bar=2
,但失败 - 正则表达式每次回溯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,')
>>>