PHP Conference Nagoya 2025

Sub-patrones de una sóla aplicación

Con las repeticiones maximizadoras y minimizadoras, el fallo de lo que se encuentra a continuación causa normalmente que la repetición del elemento sea re-evaluada para ver si un número diferente de repeticiones permite que el resto del patrón coincida. A veces es útil prevenir esto, tanto para cambiar la naturaleza de la comparación, como para ocasionar que falle antes (ya que de otra manera ocurriría el fallo después) cuando el autor del patrón sabe que no tiene sentido seguir.

Considere, por ejemplo, el patrón \d+foo cuando se aplica a la línea objetivo 123456bar

Después de coincidir los 6 dígitos y luego fallar al coincidir con "foo", la acción habitual del comparador es volver a intentarlo con sólo 5 dígitos, coincidiendo con el elemento \d+, y después con 4, y así sucesivamente, antes de que, por último, falle. Los sub-patrones de una sóla aplicación proporcionan el medio para especificar que una vez que una porción del patrón ha coincidido, no será re-evaluado de esta forma, por lo que el comparador se rendirá inmediatamente al fallar la comparación de "foo" la primera vez. La notación es otro tipo de paréntesis especiales, comenzando con (?> como en este ejemplo: (?>\d+)bar

Este tipo de paréntesis "bloquea" la parte del patrón que lo contiene una vez haya coincidido, y un fallo dentro del patrón evita que éste retroceda sobre sí mismo. El retroceso hacia elementos previos al sub-patrón funciona normalmente, después de todo.

Una descripción alternativa es que un sub-patrón de este tipo coincide con la cadena de caracteres que coincidiría con un patrón idéntico independiente, si está anclado en el punto actual de la cadena objetivo.

Los sub-patrones de una sóla aplicación no son sub-patrones de captura. Los casos sencillos como el anterior se pueden tomar como una repetición maximizadora que debe tragar todo lo que pueda. Así, mientras que \d+ y \d+? están preparados para adaptarse al número de dígitos que comparan para hacer que el resto del patrón coincida, (?>\d+) sólo puede conincidir una secuencia completa de dígitos.

Esta construcción puede, por supuesto, contener arbitrariamente sub-patrones complicados, y pueden estar anidados.

Los sub-patrones de una sóla aplicación se pueden usar junto con declaraciones de búsqueda hacia atrás para especificar una comparación eficiente al final de la cadena objetivo. Considere un simple patrón como abcd$ cuando se aplica a una cadena larga la cual no coincide. Ya que la comparación se lleva a cabo de izquierda a derecha, PCRE buscará cada "a" en el sujeto y después mirará si lo siguiente coincide con el resto del patrón. Si el patrón se especifica así ^.*abcd$ el .* inicial coincide con la cadena entera primero, pero cuando esto falla (debido a que no hay una "a" siguiente), retrocede para comparar todos los caracteres excepto el último, después todos excepto los dos últimos, y así sucesivamente. Una vez más la búsqueda de "a" cubre la cadena entera, de derecha a izquierda, por lo que no estamos en mejores circunstancias. Sin embargo, si el patrón se escribe así ^(?>.*)(?<=abcd) entonces no puede haber retroceso para el elemento .* ; sólo puede coincidir con la cadena entera. La subsiguiente declaración de búsqueda hacia atrás realiza una única comprobación sobre los últimos cuatro caracteres. Si falla, la comparación falla inmediatamente. Para cadenas largas, este enfoque produce una diferencia significativa en el tiempo de procesamiento.

Cuando un patrón contiene una repetición ilimitada dentro de un sub-patrón que puede repetirse un número ilimitado de veces, el uso de un sub-patrón de una sóla aplicación es la única manera de evitar que algunas comparaciones fallidas tomen mucho tiempo. El patrón (\D+|<\d+>)*[!?] compara un número ilimitado de subcadenas que consisten en algo que no sea un dígito, o dígitos encerrados entre <>, seguido de ! o ?. Cuando coincide, se ejecuta rápidamente. Sin embargo, si se aplica a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa toma mucho tiempo antes de informar del fallo. Esto es porque la cadena puede ser dividida entre las dos repeticiones de muchas maneras, y todo tiene que ser probado. (En el ejemplo se usó [!?] en vez de un carácter único al final, ya que PCRE y Perl están optimizados de forma que permiten que se falle rápidamente cuando se usa un carácter único. Ambos recuerdan el último carácter único que se requiere para una coincidencia, y falla antes si no está presente en la cadena.) Si el patrón se cambia a ((?>\D+)|<\d+>)*[!?] las secuencias de los "no-dígitos" no pueden romperse, y el fallo ocurre rápidamente.

add a note

User Contributed Notes 1 note

up
4
Anonymous
2 years ago
Never put a "once-only subpattern" (?>...) in a "one-line" comment (#, //).
I spent almost 1 day to fix it.
Use a 'C' style comment instead !

PHP Manual says, a "one-line" comment (# , //) ends just before "?>" (PHP end tag).

These letters "?>" in a "one-line" comment (# , //) seems evaluated as a PHP end tag.

<?php

/* (?> */
echo '"C" style comment works !<br>';

# (?>
echo '"one-line" comment';

?>
To Top