问题
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);
我稍微复杂了模式,在多个X
s 周围添加了一个捕获组。这是我的结果:
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
s后面的 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)+
被允许 ?
任何见解都非常感谢。谢谢你。