¿Existe un comportamiento definido sobre cómo las expresiones regulares deben manejar el comportamiento de captura de paréntesis anidados? Más específicamente, ¿puede esperar razonablemente que diferentes motores capturen los paréntesis externos en la primera posición y los paréntesis anidados en las posiciones posteriores?
Considere el siguiente código PHP (usando expresiones regulares PCRE)
<?php
$test_string = 'I want to test sub patterns';
preg_match('{(I (want) (to) test) sub (patterns)}', $test_string, $matches);
print_r($matches);
?>
Array
(
[0] => I want to test sub patterns //entire pattern
[1] => I want to test //entire outer parenthesis
[2] => want //first inner
[3] => to //second inner
[4] => patterns //next parentheses set
)
La expresión completa entre paréntesis se captura primero (quiero probar), y luego los patrones internos entre paréntesis se capturan a continuación ("quiero" y "para"). Esto tiene sentido lógico, pero pude ver un caso igualmente lógico para capturar primero los subparéntesis y LUEGO capturar el patrón completo.
Entonces, ¿este comportamiento definido de "capturar todo primero" en motores de expresión regular, o va a depender del contexto del patrón y / o del comportamiento del motor (PCRE es diferente de C # es diferente de Java es diferente que etc.)?
Respuestas:
De perlrequick
Advertencia : excluyendo el paréntesis de apertura del grupo sin captura (? =)
Actualizar
No uso mucho PCRE, ya que generalmente uso lo real;), pero los documentos de PCRE muestran lo mismo que los de Perl:
Si PCRE se está alejando de la compatibilidad de expresiones regulares de Perl, quizás el acrónimo debería redefinirse: "Expresiones regulares afines de Perl", "Expresiones regulares comparables de Perl" o algo así. O simplemente deshacerse de las letras de significado.
fuente
Sí, todo esto está bastante bien definido para todos los idiomas que le interesan:
"Los grupos de captura se numeran contando sus paréntesis de apertura de izquierda a derecha. ... Grupo cero siempre representa la expresión completa ".
"Las capturas que usan () se numeran automáticamente según el orden del paréntesis de apertura, comenzando desde uno. El primero capturar, capturar elemento número cero, es el texto que coincide con el patrón de expresión regular completo. ")
"\ 0 o $ 0 se refiere al texto que coincide con el patrón completo. Los paréntesis de apertura se cuentan de izquierda a derecha (comenzando desde 1) para obtener el número del subpatrón de captura ". (También fue cierto para las funciones POSIX obsoletas)
PCRE - http://www.pcre.org/pcre.txt
Para agregar a lo que dijo Alan M, busque "Cómo pcre_exec () devuelve subcadenas capturadas" y lea el quinto párrafo que sigue:
$ 1, $ 2, etc., coinciden con los grupos de captura como se esperaría (es decir, por la aparición del corchete de apertura), sin embargo $ 0 devuelve el nombre del programa, no la cadena de consulta completa - para conseguir que use $ & en su lugar.
Es muy probable que encuentre resultados similares para otros lenguajes (Python, Ruby y otros).
Dice que es igualmente lógico enumerar los grupos de captura internos primero y tiene razón: es solo una cuestión de indexar al cerrar, en lugar de abrir, parens. (si te entiendo bien). Sin embargo, hacer esto es menos natural (por ejemplo, no sigue la convención de dirección de lectura) y, por lo tanto, hace que sea más difícil (probablemente no significativamente) determinar, por insepección, qué grupo de captura estará en un índice de resultado dado.
Poner toda la cadena de coincidencias en la posición 0 también tiene sentido, principalmente por coherencia. Permite que toda la cadena coincidente permanezca en el mismo índice independientemente del número de grupos de captura de expresión regular a expresión regular e independientemente del número de grupos de captura que realmente coincidan con cualquier cosa (Java, por ejemplo, colapsará la longitud de la matriz de grupos coincidentes para cada captura grupo no coincide con ningún contenido (piense, por ejemplo, algo como "un patrón (. *)"). Siempre puede inspeccionar capturing_group_results [capturing_group_results_length - 2], pero eso no se traduce bien en los idiomas de Perl que crean variables de forma dinámica ($ 1 , $ 2 etc.) (Perl es un mal ejemplo, por supuesto, ya que usa $ & para la expresión coincidente, pero entiendes la idea :).
fuente
Cada sabor de expresión regular que conozco agrupa los grupos por el orden en que aparecen los paréntesis de apertura. El hecho de que los grupos externos se enumeren antes de sus subgrupos contenidos es solo un resultado natural, no una política explícita.
Donde se pone interesante es con los grupos con nombre . En la mayoría de los casos, siguen la misma política de numeración por las posiciones relativas de los paréntesis: el nombre es simplemente un alias para el número. Sin embargo, en las expresiones regulares de .NET, los grupos con nombre se numeran por separado de los grupos numerados. Por ejemplo:
Regex.Replace(@"one two three four", @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)", @"$1 $2 $3 $4") // result: "two four one three"
En efecto, el número es un alias del nombre ; los números asignados a los grupos nombrados comienzan donde terminan los grupos numerados "reales". Eso puede parecer una política extraña, pero hay una buena razón para ello: en las expresiones regulares de .NET puede usar el mismo nombre de grupo más de una vez en una expresión regular. Eso hace posibles expresiones regulares como la de este hilo para hacer coincidir números de punto flotante de diferentes configuraciones regionales:
^[+-]?[0-9]{1,3} (?: (?:(?<thousand>\,)[0-9]{3})* (?:(?<decimal>\.)[0-9]{2})? | (?:(?<thousand>\.)[0-9]{3})* (?:(?<decimal>\,)[0-9]{2})? | [0-9]* (?:(?<decimal>[\.\,])[0-9]{2})? )$
Si hay un separador de miles, se guardará en el grupo "mil" sin importar qué parte de la expresión regular coincida con él. Del mismo modo, el separador decimal (si lo hay) siempre se guardará en el grupo "decimal". Por supuesto, hay formas de identificar y extraer los separadores sin grupos con nombre reutilizables, pero de esta forma es mucho más conveniente, creo que justifica con creces el extraño esquema de numeración.
Y luego está Perl 5.10+, que nos da más control sobre la captura de grupos del que sé qué hacer. :RE
fuente
El orden de captura en el orden del par izquierdo es estándar en todas las plataformas en las que he trabajado (perl, php, ruby, egrep)
fuente