¿Por qué re.sub (‘. *?’, ‘-‘, ‘abc’) devuelve ‘-abc-‘ en lugar de ‘——-‘?

Este es el resultado de python2.7.

>>> re.sub('.*?', '-', 'abc') '-abc-' 

Los resultados que pensé deberían ser los siguientes.

 >>> re.sub('.*?', '-', 'abc') '-------' 

Pero no lo es. ¿Por qué?

La mejor explicación de este comportamiento que conozco es del paquete regex PyPI, que está destinado a reemplazar eventualmente a re (aunque ha sido así desde hace mucho tiempo).

A veces no está claro cómo deben manejarse las coincidencias de ancho cero. Por ejemplo, ¿debe. * Coincidir con 0 caracteres directamente después de hacer coincidir> 0 caracteres?

La mayoría de las implementaciones de expresiones regulares siguen el ejemplo de Perl (PCRE), pero a veces el módulo re no lo hace. El comportamiento de Perl parece ser el más común (y el módulo re es a veces definitivamente incorrecto), por lo que en la versión 1 el módulo regex sigue el comportamiento de Perl, mientras que en la versión 0 sigue el comportamiento re tradicional.

Ejemplos:

 # Version 0 behaviour (like re) >>> regex.sub('(?V0).*', 'x', 'test') 'x' >>> regex.sub('(?V0).*?', '|', 'test') '|t|e|s|t|' # Version 1 behaviour (like Perl) >>> regex.sub('(?V1).*', 'x', 'test') 'xx' >>> regex.sub('(?V1).*?', '|', 'test') '

(?VX) establece el indicador de versión en la expresión regular. El segundo ejemplo es lo que usted espera, y es supuestamente lo que hace PCRE. La referencia de Python es algo no estándar, y se mantiene ya que probablemente se deba únicamente a problemas de compatibilidad con versiones anteriores. He encontrado un ejemplo de algo similar (con re.split ).

Para su nueva pregunta editada:

El .*? puede coincidir con cualquier número de caracteres, incluyendo cero. Entonces, lo que hace es que coincide con cero caracteres en cada posición de la cadena: antes de la “a”, entre la “a” y la “b”, etc. Reemplaza cada una de esas coincidencias de ancho cero con un guión, dando el resultado Lo ves.

La expresión regular no intenta hacer coincidir cada carácter uno por uno; trata de coincidir en cada posición en la cadena. Tu expresión regular permite que coincida con cero caracteres. Entonces coincide con cero en cada posición y se mueve a la siguiente. Parece que estás pensando que en una cadena como “abc” hay una posición antes de la “b”, una posición “dentro de” la “b” y una posición después de “b”, pero no hay una posición “dentro” “Un personaje individual. Si coincide con cero caracteres que comienzan antes de “b”, lo siguiente que intentará es hacer coincidir el inicio después de “b”. No hay forma de obtener una expresión regular que coincida siete veces en una cadena de tres caracteres, porque solo hay cuatro posiciones para coincidir.

¿Está seguro de haber interpretado correctamente la documentación de re.sub ?

*? , +? , ?? El ‘ ‘, ‘+’, y ‘?’ los calificativos son todos codiciosos; hacen coincidir tanto texto como sea posible. A veces este comportamiento no es deseado; si el RE <. > coincide con '

title

' , coincidirá con toda la cadena y no solo con '

'

. Añadiendo ‘?’ después de que el clasificado lo haga realizar el partido de manera no codiciosa o mínima; Se emparejarán tan pocos caracteres como sea posible. Utilizando .*? en la expresión anterior solo coincidirá con ”.

Añadiendo un ? convertirá la expresión en una no codiciosa.

Codicioso:

 re.sub(".*", "-", "abc") 

no codicioso:

 re.sub(".*?", "-", "abc") 

Actualización: FWIW re.sub hace exactamente lo que debería:

 >>> from re import sub >>> sub(".*?", "-", "abc") '-abc-' >>> sub(".*", "-", "abc") '-' 

Vea la asombrosa respuesta de @BrenBarn sobre por qué obtiene -abc- 🙂

Aquí hay una representación visual de lo que está pasando:

 .*? 

Visualización de expresiones regulares

Demo Debuggex

Para elaborar la respuesta de Veedrac , la implementación diferente tiene un tratamiento diferente de las coincidencias de ancho cero en las operaciones FindAll (o ReplaceAll). Se pueden observar dos comportamientos entre diferentes implementaciones, y Python simplemente elige seguir la primera línea de implementación.

1. Salta siempre con un carácter en una coincidencia de ancho cero

En Java y JavaScript, la coincidencia de ancho cero hace que el índice se desplace a lo largo de un carácter, ya que permanecer en el mismo índice provocará un bucle infinito en las operaciones Buscar o Reemplazar todo.

Como resultado, la salida de las operaciones FindAll en dicha implementación puede contener como máximo 1 coincidencia a partir de un índice particular.

El paquete predeterminado de Python re probablemente también siga la misma implementación (y parece ser también el caso de Ruby).

2. No permitir la coincidencia de ancho cero en la próxima coincidencia en el mismo índice

En PHP, que proporciona una envoltura sobre libreary de PCRE, la coincidencia de ancho cero no hace que el índice aumente inmediatamente. En su lugar, establecerá una PCRE_NOTEMPTY ( PCRE_NOTEMPTY ) que requiere que la próxima coincidencia (que comienza en el mismo índice) sea una coincidencia de ancho no cero. Si la coincidencia tiene éxito, se desplazará a lo largo de la longitud de la coincidencia (distinta de cero); De lo contrario, se topa con un personaje.

Por cierto, la biblioteca PCRE no proporciona una operación integrada de FindAll o ReplaceAll. En realidad es proporcionado por envoltura de PHP.

Como resultado, la salida de las operaciones FindAll en dicha implementación puede contener hasta 2 coincidencias comenzando en el mismo índice.

El paquete de regex Python probablemente sigue esta línea de implementación.

Esta línea de implementación es más compleja, ya que requiere la implementación de FindAll o ReplaceAll para mantener un estado adicional de rechazar o no la coincidencia de ancho cero. El desarrollador también debe realizar un seguimiento de estos indicadores adicionales cuando utilizan la API de bajo nivel de coincidencia.