如果重复出现在括号中,则编译可变长度查找.为什么?


Variable-length lookbehind compiles if repetition is in parentheses. Why?

问题

PHP使用PCRE正则表达式库,该库不支持lookbehinds中的重复。

如果在后备中有重复(例如(?<='d+)),PHP通常会发出这样的警告:

警告:preg_match_all():编译失败:在第10行的lookbehind.php中,lookbehind断言的偏移量7处长度不固定

然而,我发现了一种情况,即编译在我认为应该失败的时候不会失败。

如预期,这些无法编译:

  • /(?<=X*)a/
  • /(?<=X+)a/
  • /(?<=(X)*)a/

但是,/(?<=(X)+)a/确实会编译。这在功能上应该等同于/(?<=(X){1,})a/,后者也进行编译。另一方面,如果我真的给这个范围加了一个上界
(例如/(?<=(X){1,2})a/),无法编译。我认为/(?<=(X)+)a//(?<=(X){1,})a/也应该编译失败,但它们没有。为什么不呢?

实验

这里有一些代码:

$str = 'aXaaXXaaaXXXaaaa';
$regex = '/(?<=((?:X)+))a+/';
preg_match_all($regex, $str, $matches, PREG_OFFSET_CAPTURE|PREG_SET_ORDER);
print_r($matches);

我稍微复杂了一下模式,在多个Xs周围添加了一个捕获组。以下是我的结果:

Array (
    [0] => Array (
            [0] => Array (
                    [0] => aa
                    [1] => 2
                )
            [1] => Array (
                    [0] => X
                    [1] => 1
                )
        )
    [1] => Array (
            [0] => Array (
                    [0] => aaa
                    [1] => 6
                )
            [1] => Array (
                    [0] => X
                    [1] => 5
                )
        )
    [2] => Array (
            [0] => Array (
                    [0] => aaaa
                    [1] => 12
                )
            [1] => Array (
                    [0] => X
                    [1] => 11
                )
        )
)

它与X之后的a s明显匹配,这是正确的。然而,子模式1似乎只匹配一个X,而不是所有CCD_13。如果我在查找的开头添加一个a,这样它就必须找到介于两者之间的所有X,下面是我的结果:

$regex = '/(?<=(a(?:X)+))a+/';
Array (
    [0] => Array (
            [0] => Array (
                    [0] => aa
                    [1] => 2
                )
            [1] => Array (
                    [0] => aX
                    [1] => 0
                )
        )
)

它只匹配一次(其中只有一个X)。实际上,(X)+(X){1,}被缩减为(X){1}(由于其固定长度,这是允许的)。

结论

我讨厌一发现不符合我预期的东西就哭,"小虫!"但它看起来确实是一个。该模式并没有像我所期望的那样被拒绝,而且即使它是一个有效的模式,它也不会像我所预期的那样表现。

所以我问:

  • 它应该这样做有正当理由吗
  • 为什么这适用于+而不适用于CCD21
  • 为什么括号很重要:X+失败;是否允许(X)+

任何见解都将不胜感激。非常感谢。

这不是PHP错误。如果它是一个错误(看起来确实像一个),那么它就是PCRE错误,应该在那里报告。但是,请检查phpinfo()中的PCRE版本,并将其与最新版本进行比较。如果它不是最新的,请在发布错误报告之前,直接在最新的PCRE中运行相同的正则表达式。

PCRE版本8.32-RC1 2012-08-08

re>/(?<=(X)+)a/失败:后备断言在偏移量8处不是固定长度re>

可能是个bug。请更新到最新的PCRE。

顺便说一句,您可以使用''K创建无限的反向引用。