无法使用反向引用的后顾断言

12

据我了解,

(.)(?<!\1)

这应该永远不会匹配。实际上,PHP的preg_replace甚至拒绝编译它,Ruby的gsub也是如此。但是Python的re模块似乎有不同的看法:

import re
test = 'xAAAAAyBBBBz'
print (re.sub(r'(.)(?<!\1)', r'(\g<0>)', test))

结果:

(x)AAAA(A)(y)BBB(B)(z)

请问是否有人可以提供一个合理的解释来说明这种行为?

更新

这种行为似乎是在re模块中的一种限制。另一个regex 模块似乎可以正确处理断言中的分组:

import regex

test = 'xAAAAAyBBBBz'

print (regex.sub(r'(.)(?<!\1)', r'(\g<0>)', test))
## xAAAAAyBBBBz

print (regex.sub(r'(.)(.)(?<!\1)', r'(\g<0>)', test))
## (xA)AAA(Ay)BBB(Bz)

请注意,与pcre不同,regex还允许可变宽度的后顾断言:
print (regex.sub(r'(.)(?<![A-Z]+)', r'(\g<0>)', test))
## (x)AAAAA(y)BBBB(z)

最终,正则表达式将会被包含在标准库中,如PEP 411所述。


3
它的匹配效果就像你使用了(.)(?!\1) - Brigand
1个回答

6

这似乎是Python re 模块的限制(从与Microsoft的支持电话中学到的,“bug”的委婉说法)。

我猜这与Python不支持可变长度的向后查找有关,但它不够聪明,无法弄清楚 \1 将始终是固定长度。为什么编译正则表达式时它不抱怨这一点,我也说不清。

有趣的是:

>>> print (re.sub(r'.(?<!\0)', r'(\g<0>)', test))
(x)(A)(A)(A)(A)(A)(y)(B)(B)(B)(B)(z)
>>>
>>> re.compile(r'(.*)(?<!\1)') # This should trigger an error but doesn't!
<_sre.SRE_Pattern object at 0x00000000026A89C0>

因此,在Python中不要在反向引用的回溯断言中使用,正向回溯也不太好(它也会像正向前瞻一样匹配这里):

>>> print (re.sub(r'(.)(?<=\1)', r'(\g<0>)', test))
x(A)(A)(A)(A)Ay(B)(B)(B)Bz

我甚至无法猜测这里发生了什么:

>>> print (re.sub(r'(.+)(?<=\1)', r'(\g<0>)', test))
x(AA)(A)(A)Ay(BB)(B)Bz

谢谢,这证实了我的观点,这是一个漏洞。 - georg
Python 显然不是唯一一个在后向引用的回顾断言中存在问题的语言:https://dev59.com/s3E85IYBdhLWcg3whD7k#2735611 - Tim Pietzcker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接