3

问题

PHP 使用PCRE正则表达式库,它不支持后向重复。

如果重复出现在后面(例如,(?<=\d+)),PHP 通常会发出如下警告:

警告:preg_match_all():编译失败:lookbehind 断言在第 10 行的lookbehind.php 中的偏移量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
                )
        )
)

它显然与as后面的 s 匹配X,这是正确的。但是,子模式 1 似乎只匹配一个X,而不是全部。如果我a在lookbehind 的开头添加一个,以便它必须找到X其间的所有 s,这是我的结果:

$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)+被允许 ?

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

4

2 回答 2

1

PCRE 版本 8.32-RC1 2012-08-08

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

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

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

于 2012-08-24T14:22:22.530 回答
1

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

于 2012-08-23T05:41:51.343 回答