A referência de fundo vazia causa falha no PHP ... existe uma solução alternativa?

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

  •  27-09-2019
  •  | 
  •  

Pergunta

Estou tendo problemas com uma expressão regular no PHP que usa uma referência de fundo potencialmente vazia. Eu esperava que funcionasse como explicado em http://www.regular-expressions.info/brackets.html:

Se uma referência de fundo não foi usada em uma tentativa de partida específica (como no primeiro exemplo em que o ponto de interrogação tornou a primeira referência de referência opcional), ela está simplesmente vazia. Usar uma referência de fundo vazia no regex está perfeitamente bem. Simplesmente será substituído por nada.

No entanto, parece que o PHP é um pouco diferente ... de http://php.net/manual/en/regexp.reference.back-references.php:

Se um subpadrão não tiver sido usado em uma correspondência específica, qualquer referência de retorno a ele sempre falha.

Como um exemplo simplificado, quero combinar as duas coisas a seguir com este regex:

  • {alguma coisa alguma coisa}
  • {algo: else} ... {/algo: else}

Onde "algo" é conhecido com antecedência, e "else" pode ser qualquer coisa (ou nada).

Então eu tentei o seguinte regex ("else" codificado por simplicidade):

preg_match("/\{(something(:else)?)\}(.*?)\{\/something\\2\}/is", $data, $matches)

Infelizmente se (: mais)? Não corresponde, a referência 2 falha. Se eu fizer 2 opcional ( 2?), Poderá corresponder a {algo} ... {algo: else}, o que não é bom.

Eu encontrei uma limitação de expressões regulares (o infame "você precisa de um analisador, não uma regex") ou isso é corrigível?

Programa de teste:

<?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);
?>
Foi útil?

Solução

Por que você simplesmente não usa 1 em vez de 2?

preg_match_all("/\{(something(:else)?)\}(.*?)\{\/\\1\}/is", $data, $matches);

Quanto a "você precisa de um problema de analisador", precisará dele para analisar construções aninhadas.

Outras dicas

Bem, por que não substituir o? com um ou?

Mudar

"/\{(something(:else)?)\}(.*?)\{\/something\\2\}/is"

Para

"/\{(something(:else|))\}(.*?)\{\/something\\2\}/is"

Dessa forma, a referência sempre será capturada, mas às vezes estará vazia (o que está ok) ...

A classe seguinte é constituída para tais casos (como {algo} ... {/something} ou {algo} ... {algo} ... {/something} {/something} e mais. Exemplo com sl5_preg_contentfinder classe

https://gist.github.com/sl5net/7029093#file-sl5_preg_contentfinder-php

        $content1 = $content = '`ha <!--[01.o0]-->1<!--[/01.o0]-->

oi [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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top