1

我在 PHP 中使用可能为空的反向引用的正则表达式遇到问题。我希望它会按照http://www.regular-expressions.info/brackets.html中的说明工作:

如果在特定的匹配尝试中未使用反向引用(例如在第一个示例中,问号使第一个反向引用可选),则它只是空的。在正则表达式中使用空的反向引用非常好。它只会被虚无所取代。

然而,PHP 似乎有点不同......来自http://php.net/manual/en/regexp.reference.back-references.php

如果子模式实际上没有在特定匹配中使用,那么对它的任何反向引用总是会失败。

作为一个简化的例子,我想用这个正则表达式匹配以下两件事:

  • {某事} ... {/某事}
  • {something:else} ... {/something:else}

其中“某事”是提前知道的,而“其他”可以是任何东西(或什么都没有)。

所以我尝试了以下正则表达式(为简单起见,硬编码“else”):

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

不幸的是 if (:else)? 不匹配,\2 反向引用失败。如果我将 \2 设为可选 (\2?),那么我可能会匹配 {something} ... {something:else},这并不好。

我是否遇到了正则表达式的限制(臭名昭著的“你需要一个解析器,而不是一个正则表达式”)还是可以修复?

测试程序:

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

3 回答 3

2

那么,为什么不更换呢?与或?

改变

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

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

这样引用将始终被捕获,但有时它会是空的(这没关系)......

于 2010-08-11T14:04:08.593 回答
1

为什么不简单地使用\1 而不是\2?

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

至于“你需要一个解析器”问题,你将/确实需要它来解析嵌套结构。

于 2010-08-11T14:09:50.873 回答
0

为此类情况构建的以下类(如 {something} ... {/something} 或 {something} ... {something} ... {/something} {/something} 等等。SL5_ preg_contentFinder类的示例

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

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

嗨 [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'];
        }
于 2013-10-17T18:24:08.037 回答