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.
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! –
@Bart - Buen punto, esa es una mala elección de palabras. ¡Gracias! – Kobi