пустая обратная ссылка вызывает ошибку совпадения в PHP ... есть ли обходной путь? - PullRequest
1 голос
/ 11 августа 2010

У меня проблемы с регулярным выражением в PHP, которое использует потенциально пустую обратную ссылку. Я надеялся, что это будет работать, как объяснено в http://www.regular -expressions.info / brackets.html :

Если обратная ссылка не использовалась в конкретная попытка совпадения (например, в первый пример, где вопрос Марк сделал первую обратную ссылку необязательно), он просто пуст. С помощью пустая обратная ссылка в регулярном выражении прекрасно. Это будет просто заменен ничем.

Однако кажется, что PHP немного отличается от http://php.net/manual/en/regexp.reference.back-references.php:

Если подшаблон фактически не был используется в конкретном матче, то любой обратные ссылки на него всегда терпят неудачу.

В качестве упрощенного примера я хочу сопоставить следующие две вещи с этим регулярным выражением:

  • {что-то} ... {/ что-то}
  • {что-то: еще} ... {/ что-то: еще}

Где «что-то» известно заранее, а «еще» может быть чем угодно (или ничем).

поэтому я попробовал следующее регулярное выражение («просто» для простоты):

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

К сожалению, если (: else)? не совпадает, обратная ссылка \ 2 завершается неудачно. Если я сделаю \ 2 необязательным (\ 2?), То я могу сопоставить {что-то} ... {что-то: еще}, что не годится.

Я столкнулся с ограничением регулярных выражений (печально известное «вам нужен анализатор, а не регулярное выражение») или это можно исправить?

Тестовая программа:

<?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);
?>

Ответы [ 3 ]

2 голосов
/ 11 августа 2010

Ну, почему бы не заменить?с или?

Изменить

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

На

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

Таким образом, ссылка всегда будет захвачена, но иногда она будет пустой (что нормально)...

1 голос
/ 11 августа 2010

Почему бы вам просто не использовать \ 1 вместо \ 2?

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

Что касается проблемы "вам нужен анализатор", вам понадобится / нужно ее проанализировать для вложенных конструкций.

0 голосов
/ 17 октября 2013

следующий класс для таких случаев (например, {что-то} ... {/ что-то} или {что-то} ... {что-то} ... {/ что-то} {/ что-то} и больше. пример с SL5_ preg_contentFinder class

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

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

привет [02.o0] 2 хо 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'];
        }
...