带有反向引用的不可能的背后隐藏

geo*_*org 12 python regex python-2.7 python-3.x

根据我的理解,

(.)(?<!\1)
Run Code Online (Sandbox Code Playgroud)

永远不应该匹配.实际上,php preg_replace甚至拒绝编译这个,所以ruby也是如此gsub.python re模块似乎有不同的意见:

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

结果:

(x)AAAA(A)(y)BBB(B)(z)
Run Code Online (Sandbox Code Playgroud)

任何人都可以为这种行为提供合理的解释吗?

更新

这种行为似乎是限制了在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)
Run Code Online (Sandbox Code Playgroud)

请注意,不同的是pcre,regex还允许可变宽度的lookbehinds:

print (regex.sub(r'(.)(?<![A-Z]+)', r'(\g<0>)', test))
## (x)AAAAA(y)BBBB(z)
Run Code Online (Sandbox Code Playgroud)

最终,regex将被包含在标准库中,如PEP 411中所述.

Tim*_*ker 6

这看起来像一个限制(在我从Python支持调用中学到的"bug"的好方法)在Python re模块中.

我猜这与Python不支持可变长度的lookbehind断言这一事实​​有关,但它并不足以弄清楚它\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>
Run Code Online (Sandbox Code Playgroud)

所以最好不要在Python中的lookbehind断言中使用反向引用.积极的外观并不是更好(它也匹配在这里好像它是一个积极的前瞻):

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

我甚至猜不出这里发生了什么:

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