为什么这个正则表达式在 PCRE (PHP < 7.3) 中失败,但在 PCRE2 (PHP >= 7.3) 中有效



正则表达式:

/(?<nn>(?!und)[^/,&;]+)(?:,s?+)(?<vn>(?1))(?:/|&|;|und|$)s?/

应该使用preg_match_all导致两个匹配

nn(1): Oidtmann-van Beek
vn(1): Jeanne 
nn(2): Oidtmann
vn(2): Peter

关于样本串Oidtmann-van Beek, Jeanne und Oidtmann, Peter

这适用于PCRE2(PHP>=7.3(

但不是用PHP<7.3,为什么?

https://regex101.com/r/zotHZN/1/

使用PCRE无法获得预期的输出,因为(?1)regex子例程是原子的,其模式无法回溯。

参见">PCRE2和Perl之间递归处理的差异":

10.30版本之前,PCRE2中的递归处理与Perl的不同之处在于,递归子例程调用始终被视为原子组。也就是说,一旦它匹配了一些主题字符串,就永远不会重新输入,即使它包含未经尝试的替代项,并且随后匹配失败。(历史注释:PCRE在Perl之前实现了递归。(

从10.30版本开始,递归子例程调用不再被视为原子调用。也就是说,如果模式稍后出现匹配失败,则可以重新输入它们以尝试未使用的替代方案。这现在与Perl的工作方式兼容了。如果希望子例程调用是原子调用,则必须将其显式地包含在原子组中。

因此,解决方案是使用模式本身,而不是子程序:

/(?<nn>(?!und)[^/,&;]+),s?+(?<vn>(?!und)[^/,&;]+)(?:/|&|;|und|$)s?/

注意,我仅用s?+替换了(?:,s?+),因为这里的非捕获组是多余的。

我认为像/b(?<nn>(?!undb)w+(?:[-'s]+(?!undb)w+)*),s?(?<vn>(?&nn))b/u这样更精确的模式在这里会更好。请参阅此regex演示。它将不需要任何回溯,因为w+(?:[-'s]+(?!undb)w+)*部分与,s?模式不重叠。

最新更新