如果重复在括号中,则可变长度的lookbehind编译。为什么?

时间:2012-08-05 04:48:10

标签: php regex pcre

问题

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

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

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

它显然与a后面的X匹配,这是正确的。但是,子模式1似乎只匹配一个X,而不是所有a。如果我在lookbehind的开头添加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}正在减少到+(由于其固定长度,这是允许的)。

结论

我不想哭,“虫子!”一旦我发现一些不符合我预期的东西,但它确实看起来像是一个。模式不会像我期望的那样被拒绝,然后即使它是一个有效的模式,它也不像我期望的那样。

所以我问:

  • 它是否应该以这种方式运作?
  • 为什么这适用于*但不适用于X+
  • 为什么括号很重要:(X)+失败;允许{{1}}?

非常感谢任何见解。谢谢。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

PCRE版本8.32-RC1 2012-08-08

重新&GT; /(&LT; =(X)+)A / 失败:lookbehind断言在偏移量8处不是固定长度   重新&GT;

可能是一个错误。请更新到最新的PCRE。

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