2011-05-03 9 views
6

EDITAR: eliminar el ejemplo original porque provocó respuestas secundarias. también arregló el título.

La cuestión es por qué la presencia de la "$" en la expresión regular efectúa la greedyness de la expresión:

Aquí está un ejemplo más simple: "?"

>>> import re 
>>> str = "baaaaaaaa" 
>>> m = re.search(r"a+$", str) 
>>> m.group() 
'aaaaaaaa' 
>>> m = re.search(r"a+?$", str) 
>>> m.group() 
'aaaaaaaa' 

El parece estar haciendo nada. Tenga en cuenta que cuando se quita el "$", sin embargo, el "?" se respeta:

>>> m = re.search(r"a+?", str) 
>>> m.group() 
'a' 

EDIT: En otras palabras, "? a + $" se cumplan todos los de la una del lugar de sólo el último, esto no es lo que esperaba. Aquí está la descripción de la expresión regular "+?" desde el python docs: "Agregar '?' después de que el calificador lo haga realizar el partido de manera no codiciosa o mínima, se emparejarán la menor cantidad posible de personajes ".

Esto no parece ser el caso en este ejemplo: la cadena "a" coincide con la expresión regular "a +? $", Entonces ¿por qué no coincide la misma expresión en la cadena "baaaaaaa" solo una sola a (la más a la derecha)?

+0

¿Te importaría aclarar un poco tu pregunta? Tengo problemas para entender exactamente qué es lo que quieres. ¿A qué te refieres con "el primer partido"? ¿Estás hablando del '. +'? – arussell84

+0

Puede haber una manera mejor de hacer esto con otra biblioteca (en el contexto de las rutas) pero esto es fundamentalmente una pregunta acerca de las expresiones regulares. – krumpelstiltskin

+0

Lo que quiero decir con la primera coincidencia es la primera búsqueda(), la editaré. – krumpelstiltskin

Respuesta

4

Las coincidencias se "ordenan" por "left-most, then longest"; sin embargo, "más largo" es el término utilizado antes de que se permitiera el no codicioso, y en su lugar significa algo así como "número de repeticiones preferidas para cada átomo". Ser el jugador de la izquierda es más importante que el número de repeticiones. Por lo tanto, "a +? $" No coincidirá con la última A en "baaaaa" porque la coincidencia en la primera A comienza antes en la cadena.

(respuesta cambió después de OP aclaración en los comentarios. Ver la historia para el texto anterior.)

+0

@krumpelstiltskin: Eso fue un error de copiar y pegar. Originalmente usé la coincidencia en lugar de la búsqueda, volví a leer tu pregunta, luego agregué en 'c' y olvidé reemplazar dos de ellas. –

+0

No creo que esto explique por qué la búsqueda() no respeta el '?' en mi ejemplo anterior. – krumpelstiltskin

+0

@krumpelstiltskin: Sí, pero tal vez el nuevo párrafo anterior lo explicará mejor. –

1

Hay dos cuestiones en juego, aquí. Usó group() sin especificar un grupo, y puedo decir que se confunde entre el comportamiento de las expresiones regulares con un grupo explícitamente entre paréntesis y sin un grupo entre paréntesis. Este comportamiento sin entre paréntesis que está observando es solo un acceso directo que proporciona Python, y necesita leer la documentación en group() para comprenderlo por completo.

>>> import re 
>>> string = "baaa" 
>>> 
>>> # Here you're searching for one or more `a`s until the end of the line. 
>>> pattern = re.search(r"a+$", string) 
>>> pattern.group() 
'aaa' 
>>> 
>>> # This means the same thing as above, since the presence of the `$` 
>>> # cancels out any meaning that the `?` might have. 
>>> pattern = re.search(r"a+?$", string) 
>>> pattern.group() 
'aaa' 
>>> 
>>> # Here you remove the `$`, so it matches the least amount of `a` it can. 
>>> pattern = re.search(r"a+?", string) 
>>> pattern.group() 
'a' 

El fondo es que la cadena a+? coincide con uno a, y punto. Sin embargo, a+?$ coincide con a 's hasta el final de la línea. Tenga en cuenta que sin una agrupación explícita, le resultará difícil conseguir que ? signifique nada en absoluto. En general, es mejor ser explícito sobre lo que está agrupando con paréntesis, de todos modos. Déjame darte un ejemplo con grupos explícitos.

>>> # This is close to the example pattern with `a+?$` and therefore `a+$`. 
>>> # It matches `a`s until the end of the line. Again the `?` can't do anything. 
>>> pattern = re.search(r"(a+?)$", string) 
>>> pattern.group(1) 
'aaa' 
>>> 
>>> # In order to get the `?` to work, you need something else in your pattern 
>>> # and outside your group that can be matched that will allow the selection 
>>> # of `a`s to be lazy. # In this case, the `.*` is greedy and will gobble up 
>>> # everything that the lazy `a+?` doesn't want to. 
>>> pattern = re.search(r"(a+?).*$", string) 
>>> pattern.group(1) 
'a' 

Editar: Eliminado el texto en relación con las versiones antiguas de la cuestión.

+0

bueno ... realmente quiero solo la parte inicial, entonces estoy usando re.sub() para deshacerme de la subcadena correspondiente. – krumpelstiltskin

+0

¿viste cómo cambia el tamaño de la coincidencia con la inclusión/exclusión de "$"? este es el punto curioso – krumpelstiltskin

+0

@krumpelstiltskin: ver mi respuesta –

0

A menos que su pregunta no incluya información importante, no necesita, y no debe usar, expresiones regulares para esta tarea.

>>> import os 
>>> p = "/we/shant/see/this/butshouldseethis" 
>>> os.path.basename(p) 
butshouldseethis 
1

respuesta a la pregunta original:

¿Por qué la primera búsqueda() Periodo de múltiple "/" s en lugar de tomar el partido más corto?

Un sub-patrón no expansivo se llevará a la más corta consistente con el patrón completo éxito. En su ejemplo, el último subpatrón es $, por lo que los anteriores deben extenderse hasta el final de la cadena.

respuesta a la pregunta revisada:

Un sub-patrón no expansivo se llevará a la más corta consistente con el patrón completo éxito.

Otra forma de verlo: un subpatrón no codicioso coincidirá inicialmente con la coincidencia más corta posible. Sin embargo, si esto hace que el patrón completo falle, se reintentará con un carácter adicional. Este proceso continúa hasta que falla el subconjunto (lo que hace que el patrón completo falle) o todo el patrón coincide.

+0

Esta respuesta está desactualizada porque simplifiqué el ejemplo. – krumpelstiltskin

+0

@krumpelstiltskin: La única parte que está desactualizada es la cita de su pregunta original. El resto básicamente da una respuesta correcta a ambas preguntas; sin embargo, la actualizaré. –

+0

@Johh Machin: pero todo el patrón tiene éxito "a +? $" En el último carácter "a" de la cadena "baaaaaaaa". – krumpelstiltskin

4

El modificador no sólo afecta a los codiciosos, donde el partido se detiene , nunca se donde entra en aperturas. Si desea iniciar la partida lo más tarde posible, deberá agregar .+? al comienzo de su patrón.

Sin la $, su patrón es permitió a ser menos codiciosos y detener cuanto antes, ya que no tiene que coincidir con el final de la cadena.

EDIT:

Más detalles ... En este caso:

re.search(r"a+?$", "baaaaaaaa") 

el motor de expresiones regulares ignorarán todo hasta la primera 'a', porque así es como re.search obras. Coincidirá con el primer a, y "querría" devolver una coincidencia, excepto que aún no coincide con el patrón porque debe alcanzar una coincidencia para el $. Entonces sigue comiendo el a uno a la vez y buscando $. Si fuera codicioso, no verificaría el $ después de cada a, pero solo después de que no podría coincidir con a.

Pero en este caso:

re.search(r"a+?", "baaaaaaaa") 

el motor de expresiones regulares comprobará si tiene un partido completo después de comer el primer partido (porque es no expansivo) y éxito porque no hay $ en este caso.

+0

Esta es la mejor respuesta que he visto, ya que es una descripción de lo que está sucediendo. Sin embargo, no explica por qué está sucediendo. – krumpelstiltskin

3

La presencia del $ en la expresión regular no afecta la codicia de la expresión.Simplemente agrega otra condición que debe cumplirse para que la coincidencia general tenga éxito.

Tanto a+ como a+? deben consumir el primer a que encuentren. Si ese a es seguido por más a, a+ sigue adelante y los consume también, mientras que a+? se conforma con solo uno. Si hubiera algo más para la expresión regular, a+ estaría dispuesto a conformarse con menos a, y a+? consumiría más, si eso es lo que se necesitaba para lograr una coincidencia.

Con a+$ y a+?$, que ha añadido otra condición: que coincida con al menos un a seguido por el final de la cadena. a+ todavía consume todos los a 's inicialmente, y luego pasa al ancla ($). Eso tiene éxito en el primer intento, por lo que a+ no está obligado a devolver ninguno de sus a.

Por otro lado, a+? inicialmente consume solo el a antes de entregar a $. Eso falla, por lo que el control se devuelve al a+?, que consume otro a y se vuelve a desconectar. Y así sucesivamente, hasta que a+? consuma el último a y $ finalmente tiene éxito. Así que sí, a+?$ hace coincidir el mismo número de a como , pero lo hace a regañadientes, no con avidez.

En cuanto a la regla más a la izquierda más larga que se mencionó en otra parte, eso nunca se aplicó a sabores de expresiones regulares derivadas de Perl como Python. Incluso sin cuantificadores reacios, siempre podrían devolver una coincidencia menor a la máxima gracias a ordered alternation. Creo que Jan tiene la idea correcta: los sabores derivados de Perl (o regex) se deben llamar eager, no codiciosos.

Creo que la regla más larga más a la izquierda solo se aplica a las expresiones faciales POSIX NFA, que usan motores NFA bajo el capó, pero se requieren para devolver los mismos resultados que una DGE (texto dirigido).

+0

¡Eso es todo! La regla más a la izquierda es la razón. Esta respuesta es larga sin embargo. ¿Hay alguna posibilidad de que puedas acortarlo solo para decir que coincide con la subcadena de mayor coincidencia que está más a la izquierda? ¿Hay algún enlace a alguna documentación de Python que diga esto? – krumpelstiltskin

+0

Encontré hilo similar hablando de esta situación, pero la respuesta no tiene ningún vínculo con la documentación correspondiente: [aquí] (http://stackoverflow.com/questions/2148700/how-do-i-find-the-shortest- overlapping-match-using-regular-expressions) – krumpelstiltskin

+0

Sin embargo, eso es todo: un motor dirigido por expresiones regulares como Python's * no * aguanta la coincidencia más larga. Toma la primera coincidencia que encuentra, incluso si hay coincidencias más largas disponibles desde el mismo punto de partida. Y nada de esto es exclusivo de Python. PHP, Java, .NET: todos los sabores populares derivados de Perl funcionan de la misma manera. ¿Seguiste el segundo enlace en mi respuesta? Jan hace un buen trabajo al explicar estos problemas aquí: http://www.regular-expressions.info/engine.html –

Cuestiones relacionadas