2011-08-29 16 views

Respuesta

12

Algunas ventajas:

[^>]*:

  • Más expresiva.
  • Captura líneas nuevas independientemente de /s bandera.
  • Considerado más rápido, porque el motor no tiene que retroceder para encontrar una coincidencia exitosa (con [^>] el motor no toma decisiones; solo le damos una manera de hacer coincidir el patrón con la cadena).

.*?

  • No "duplicación de código" - el carácter final sólo aparece una vez.
  • En casos más simples, el delimitador final tiene más de un carácter de longitud. (una clase de carácter no funcionaría en este caso) Una alternativa común es (?:(?!END).)*. Esto es aún peor si el delimitador END es otro patrón.
+3

Tenga en cuenta que '[^>] *' solo _no_ retrocederá si va seguido de lo que está dentro de la clase negada ('[^>] *>' en este caso). Kobi, sé que lo sabes y probablemente quise decir esto, pero quería asegurarte de que los demás no pensaran que '[^>] *' y '[^>] * +' (posesivo) son lo mismo. Además de eso, buena respuesta! –

+0

@Bart - Buen punto, esa es una mala elección de palabras. ¡Gracias! – Kobi

7

El primero es más explícito, i. mi. definitivamente excluye el delimitador de cierre de ser parte del texto coincidente. Esto no está garantizado en el segundo caso (si la expresión regular se extiende para que coincida con más que solo esta etiqueta).

Ejemplo: Si se intenta hacer coincidir <tag1><tag2>Hello! con <.*?>Hello!, la expresión regular coincidirá con

<tag1><tag2>Hello! 

mientras que <[^>]*>Hello! coincidirá con

<tag2>Hello! 
+0

Buen ejemplo de que, en ciertas circunstancias, las coincidencias reacias pueden coincidir con dos subcadenas en las que muchas personas esperan que coincida con una sola. –

+0

+1, gran ejemplo. Fue realmente difícil elegir una respuesta esta vez, pero tomé Kobis, ya que enumera los pros y los contras de ambas opciones. – Heinzi

6

Lo que muchos no tienen en cuenta al acercarse a este tipo de preguntas es lo sucede cuando la expresión regular no puede encontrar una coincidencia. Eso es cuando es más probable que aparezcan los sumideros de rendimiento asesino. Por ejemplo, tome el ejemplo de Tim, donde está buscando algo como <tag>Hello!. Consideremos lo que sucede con:

<.*?>Hello! 

El motor de expresiones regulares encuentra un < y rápidamente se encuentra un cierre >, pero no >Hello!. Entonces, .*? continúa buscando > que es seguido de Hello!.Si no hay uno, llegará hasta el final del documento antes de que se rinda. Luego, el motor de expresiones regulares reanuda el escaneo hasta que encuentra otro <, y lo intenta de nuevo. Nosotros ya sabemos cómo va a resultar, pero el motor de expresiones regulares, por lo general, no; pasa por el mismo rigamarole con cada < en el documento. Consideremos ahora la otra expresión regular:

<[^>]*>Hello! 

Al igual que antes, coincide rápidamente de la < a la >, pero no coincide con Hello!. Retrocederá al <, luego saldrá y comenzará a buscar otro <. Seguirá revisando cada < como lo hizo la primera expresión regular, pero no buscará hasta el final del documento cada vez que encuentre una.

Pero es incluso peor que eso. Si lo piensas, .*? es efectivamente equivalente a un lookahead negativo. Dice "Antes de consumir el siguiente personaje, asegúrate de que el resto de la expresión regular no coincida en esta posición". En otras palabras,

/<.*?>Hello!/ 

... es equivalente a:

/<(?:(?!>Hello!).)*(?:>Hello!|\z(*FAIL))/ 

Así que en cada posición que se está realizando, no sólo un intento de partido normal, pero una búsqueda hacia delante mucho más caro. (Es por lo menos dos veces más costoso, ya que la búsqueda hacia delante tiene que escanear al menos un carácter, entonces el . sigue adelante y consume un carácter.)

((*FAIL) es uno de backtracking-control verbs (también apoyado en PHP). |\z(*FAIL) medios de Perl "o llegar al final del documento y darse por vencido".)

Finalmente, existe otra ventaja del enfoque de clase de carácter negado. Si bien no lo hace (como se señaló @Bart) actuar como el cuantificador es posesivo, no hay nada que le impida hacer es posesivo, si su sabor lo admite:

/<[^>]*+>Hello!/ 

... o envolverlo en un grupo atómico:

/(?><[^>]*>)Hello!/ 

no sólo estas expresiones regulares no dar marcha atrás sin necesidad, que no tienen que guardar la información de estado que hace que dar marcha atrás posible.

+2

Buena respuesta. Un punto bastante importante aquí, sin embargo, es que la comparación de '<.*?> ¡Hola!' A '<[^>] *> ¡Hola!' No es del todo justo. En este caso, su delimitador final es '> ¡Hola!', No '>', y '[^>]' no puede manejarlo en absoluto. Intenté * referirme a eso en el último punto de mi respuesta. – Kobi

+1

Sí, al agregar 'Hello!' A la expresión regular original se cambia efectivamente el delimitador de cierre de un solo carácter a una secuencia de múltiples caracteres. Y eso convierte el '. *?'versión en un agujero negro potencial, mientras que la versión' [^>] * 'todavía funciona bien. Estoy diciendo que de forma aislada, no hay prácticamente nada para elegir entre los dos estilos; pero la expresión regular se vuelve un poco más compleja, y la elección adquiere una importancia crucial. –

Cuestiones relacionadas