retrorreferencia vacío provoca un fallo partido en PHP ... ¿hay alguna solución?
-
27-09-2019 - |
Pregunta
tengo problemas con una expresión regular en PHP que utiliza una referencia inversa potencialmente vacía. Tenía la esperanza de que iba a funcionar como se explica en http://www.regular-expressions.info/ brackets.html :
Si no se utilizó una referencia hacia atrás en una en particular intento partido (como en el primer ejemplo en el que la cuestión Mark hizo la primera referencia inversa opcional), simplemente se vacíe. Utilizando una referencia inversa vacía en la expresión regular es Perfectamente bien. Será simplemente reemplazado con nada.
Sin embargo, parece PHP es un poco diferente ... desde http : //php.net/manual/en/regexp.reference.back-references.php :
Si un sub-patrón no ha sido en realidad utilizado en un partido en particular, entonces cualquier referencias volver a ella siempre fallan.
Como un ejemplo simplificado, quiero coincidir las dos cosas siguientes con esta expresión regular:
- {algo} ... {/ algo}
- {algo: else} ... {/ algo: else}
Cuando "algo" es conocido de antemano, y "otro" puede ser cualquier cosa (o nada).
así que probé la siguiente expresión regular ( "si no" hardcoded para simplificar):
preg_match("/\{(something(:else)?)\}(.*?)\{\/something\\2\}/is", $data, $matches)
Desafortunadamente, si (: los demás)? no coincide, el \ 2 retrorreferencia falla. Si hago \ 2 opcional (\ 2?), Entonces podría coincidir {algo} ... {algo: else}., Que no es bueno
¿He topado con una limitación de las expresiones regulares (el famoso "se necesita un programa de análisis, no una expresión regular") o esto es corregible?
Programa de prueba:
<?php
$data = "{something} ... {/something}
{something:else} ... {/something:else}
{something:else} ... {/something}";
// won't match {something} ... {/something}
preg_match_all("/\{(something(:else)?)\}(.*?)\{\/something\\2\}/is", $data, $matches);
print_r($matches);
// change \\2 to \\2? and it matches too much
preg_match_all("/\{(something(:else)?)\}(.*?)\{\/something\\2?\}/is", $data, $matches);
print_r($matches);
?>
Solución
¿Por qué no sólo tiene que utilizar \ 1 en lugar de \ 2?
preg_match_all("/\{(something(:else)?)\}(.*?)\{\/\\1\}/is", $data, $matches);
que "se necesita un analizador" problema, se quiere / necesidad que hacer para analizar las construcciones anidadas.
Otros consejos
Bueno, ¿por qué no sustituir el? con una o?
Cambiar
"/\{(something(:else)?)\}(.*?)\{\/something\\2\}/is"
a
"/\{(something(:else|))\}(.*?)\{\/something\\2\}/is"
De esa manera siempre será capturado la referencia, pero a veces estar vacío (que está bien) ...
siguiente clase en Constucted para estos casos (como {Algo} ... {/} o {algo algo ...} {algo} ... {/} {algo / algo} y más. ejemplo, con SL5_ preg_contentFinder clase
https://gist.github.com/sl5net/7029093#file- sl5_preg_contentfinder-php
$content1 = $content = '`ha <!--[01.o0]-->1<!--[/01.o0]-->
hi [02.o0] 2 ho 3 ` ';
$pos_of_next_search = 0;
$begin = '(<!--)?\[([^\]>]*\.o0)\](-->)?';
$end = '<!--\[\/($2)\]-->';
$cf = new SL5_preg_contentFinder($content);
$cf->setBeginEnd_RegEx($begin, $end);
$cf->setSearchMode('use_BackReference_IfExists_()$1${1}');
$loopCount = 0;
while ($loopCount++ < 5) {
$cf->setPosOfNextSearch($pos_of_next_search);
list($findPos['begin_begin'], $findPos['end_begin'],
$findPos['begin_end'], $findPos['end_next'], $matchesReturn) = $cf->get_borders_left(__LINE__);
$content = $cf->getContent();
$expectedContent = $maxLoopCount;
if ($maxLoopCount>3)$expectedContent = '';
if ($content != $expectedContent)
die(__LINE__ . 'ERROR : $content != $expectedContent :' . " '$content'!= '$expectedContent ");
if (is_null($findPos['begin_begin'])) {
break;
}
echo(__LINE__ . ': '.$content1.' ==> "' . $content . '"');
$pos_of_next_search = $findPos['end_next'];
}