在研究this question的答案时,我想到了这个正则表达式:
(?:(?!\2)(?:,foo=([^,]*),(?=())|.))*\2bar=2
(注意:此正则表达式需要PyPI regex
module)
(简短说明:正则表达式基于以下事实:先行捕获组在匹配一次后便无法更改其值,因此在找到第一个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')
>>>
但是如果在foo=
之后出现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
模块中的错误吗?
答案 0 :(得分:0)
这实际上是很合理的。出现这种情况的原因很简单:回溯。
事件的顺序如下:
(?:...)*
每次前进一个字符,直到最终在foo=
找到,foo=3,
的地方bar=2
,但这失败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,')
>>>