2

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

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

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

(简短的解释:正则表达式依赖于这样一个事实,即前瞻中的捕获组在匹配一次后无法更改其值,因此在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= 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模块中的错误吗?

4

1 回答 1

0

这实际上是完全合理的。这种行为的原因很简单:回溯。

事件顺序如下:

  1. 贪婪组(?:...)*一次推进一个字符,直到它最终找到出现foo=at,foo=3,
  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,')
>>>
于 2018-10-28T23:11:43.980 回答