Recuperación del manejo de python 3.6 de re.sub () con coincidencias de longitud cero en python 3.7

El manejo de coincidencias de longitud cero ha cambiado con Python 3.7. Considere lo siguiente con python 3.6 (y anterior):

>>> import re >>> print(re.sub('a*', 'x', 'bac')) xbxcx >>> print(re.sub('.*', 'x', 'bac')) x 

Obtenemos lo siguiente con python 3.7:

 >>> import re >>> print(re.sub('a*', 'x', 'bac')) xbxxcx >>> print(re.sub('.*', 'x', 'bac')) xx 

Entiendo que este es el comportamiento estándar de PCRE. Además, re.finditer () parece haber detectado siempre la coincidencia adicional:

 >>> for m in re.finditer('a*', 'bac'): ... print(m.start(0), m.end(0), m.group(0)) ... 0 0 1 2 a 2 2 3 3 

Dicho esto, estoy interesado en recuperar el comportamiento de Python 3.6 (esto es para un proyecto de hobby que implementa sed en python ).

Puedo venir con la siguiente solución:

 def sub36(regex, replacement, string): compiled = re.compile(regex) class Match(object): def __init__(self): self.prevmatch = None def __call__(self, match): try: if match.group(0) == '' and self.prevmatch and match.start(0) == self.prevmatch.end(0): return '' else: return re._expand(compiled, match, replacement) finally: self.prevmatch = match return compiled.sub(Match(), string) 

lo que da:

 >>> print(re.sub('a*', 'x', 'bac')) xbxxcx >>> print(sub36('a*', 'x', 'bac')) xbxcx >>> print(re.sub('.*', 'x', 'bac')) xx >>> print(sub36('.*', 'x', 'bac')) x 

Sin embargo, esto parece muy elaborado para estos ejemplos.

¿Cuál sería la forma correcta de implementar el comportamiento de python 3.6 para las coincidencias de longitud cero re.sub () con python 3.7?

Su solución puede estar en el huevo regex :

Introducción de huevo Regex

Esta implementación de expresiones regulares es compatible con el módulo estándar ‘re’, pero ofrece una funcionalidad adicional. El comportamiento del módulo re con coincidencias de ancho cero cambió en Python 3.7, y este módulo seguirá ese comportamiento cuando se compile para Python 3.7.


Instalación:

 pip install regex 

Uso:

Con regex , puede especificar la versión ( V0 , V1 ) con la que se comstackrá el patrón de regex, es decir:

 # Python 3.7 and later import regex >>> regex.sub('.*', 'x', 'test') 'xx' >>> regex.sub('.*?', '|', 'test') '

‘ # Python 3.6 and earlier import regex >>> regex.sub(‘(?V0).*’, ‘x’, ‘test’) ‘x’ >>> regex.sub(‘(?V1).*’, ‘x’, ‘test’) ‘xx’ >>> regex.sub(‘(?V0).*?’, ‘|’, ‘test’) ‘|t|e|s|t|’ >>> regex.sub(‘(?V1).*?’, ‘|’, ‘test’) ‘


Nota:

La versión puede indicarse mediante el indicador VERSION0 o V0 , o (?V0) en el patrón.


Fuentes:

Hilo Regex – número 2636
expresión regular 2018.11.22

De acuerdo con el 3.7 Novedades,

El comportamiento anterior se puede restaurar cambiando el patrón a r'.+' .

Consulte https://docs.python.org/3/whatsnew/3.7.html en “Cambios en la API de Python”. Parece que la solución sería, por lo tanto, modificar una expresión regular; No parece que haya una bandera que pueda pasar para re a solicitar este comportamiento.

PCRE (incluido Python 3.7+) que satisface los ejemplos originales sería:

 ^a*|a+|(? 

https://regex101.com/r/zTpV1t/3

Sin embargo, bbaacc se sustituiría por xbbxccx (en lugar de la versión 3.6 de Python de a* que produjo xbxbxcxcx ); aún podría ser lo suficientemente bueno para algunas personas.