2012-04-23 12 views
10

Desde mi entender,de búsqueda hacia atrás imposible con una referencia inversa

(.)(?<!\1) 

nunca debe coincidir. En realidad, php's preg_replace incluso se niega a compilar esto y también lo hace ruby's gsub. El módulo de Python re parece tener una opinión diferente sin embargo:

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

Resultado:

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

Puede alguien dar una explicación razonable para este comportamiento?

actualización

Este comportamiento parece ser a limitation en el módulo re. El módulo alternativa regex parece manejar grupos en afirmaciones correctamente:

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) 

Tenga en cuenta que a diferencia de pcre, regex permite también lookbehinds de ancho variable:

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

Finalmente, regex va a ser incluido en la norma biblioteca, como se menciona en PEP 411.

+3

Está a juego como si Usaste '(.) (?! \ 1)'. – FakeRainBrigand

Respuesta

5

Esto parece una limitación (una buena forma de decir "error", como aprendí de una llamada de soporte técnico de Microsoft) en el módulo Python re.

Supongo que tiene que ver con el hecho de que Python no es compatible con aserciones lookbehind de longitud variable, pero no es lo suficientemente inteligente como para darse cuenta de que \1 siempre será de longitud fija. Por qué no se queja de esto cuando compila la expresión regular, no puedo decirlo.

Curiosamente:

>>> 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> 

Así que mejor no utilizar referencias hacia atrás en las aserciones hacia atrás en Python. búsqueda hacia atrás positiva no es mucho mejor (que también coincide aquí como si fuera una búsqueda positiva hacia delante):

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

Y ni siquiera puede adivinar lo que está pasando aquí:

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

Gracias, esto confirma mis sentimientos acerca de que esto es un error. – georg

+0

Python aparentemente no es el único lenguaje que tiene problemas con la retro-referencia en las aseveraciones "lookbehind": http://stackoverflow.com/questions/2734977/backreferences-in-lookbehind/2735611#2735611 –