问题
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
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
)
)
)
它与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创建无限的反向引用。