retrorreferencia vacío provoca un fallo partido en PHP ... ¿hay alguna solución?

StackOverflow https://stackoverflow.com/questions/3459095

  •  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);
?>
¿Fue útil?

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'];
        }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top