PHP正则表达式子程序引用不起作用

时间:2018-01-26 10:16:46

标签: php regex

我有一个像这样的简单正则表达式:

@123(?:(?:(?P<test>[\s\S]*)456(?P<test1>(?P>test))789))@

它应匹配以下字符串:

123aaaa456bbbb789

但它没有。

但是,如果我用正则表达式的直接副本替换子例程引用:

@123(?:(?:(?P<test>[\s\S]*)456(?P<test1>[\s\S]*)789))@

然后它完美无缺。

我无法弄清楚为什么通过群组名称引用模式不起作用。

1 个答案:

答案 0 :(得分:2)

这里的要点是[\s\S]*是一个*量化的子模式,允许正则表达式引擎在后续子模式无法匹配时回溯, PCRE中的递归调用是原子,即当引擎用(?P>test)抓取任何0+字符时引擎无法回溯,这就是模式无法匹配的原因。

简而言之,@123(?:(?:(?P<test>[\s\S]*)456(?P<test1>(?P>test))789))@模式可以重写为

@123(?:(?:(?P<test>[\s\S]*)456(?P<test1>[\s\S]*+)789))@
                                              ^^

[\s\S]*+已匹配789,引擎无法回溯以匹配789模式部分。

请参阅PCRE docs

  

在PCRE中(与Python类似,但与Perl不同),递归子模式调用始终被视为原子组。也就是说,一旦它匹配了一些主题字符串,它就永远不会被重新输入,即使它包含未经验证的替代品并且随后存在匹配失败。

不知道为什么他们在这里提到Python,因为re不支持递归(除非他们指的是PyPi正则表达式模块)。

如果您正在寻找解决方案,可以使用(?:(?!789)[\s\S])* tempered greedy token代替[\s\S]*,它只会匹配任何字符,如果它没有启动789 char序列(因此,无需回溯以适应789):

123(?:(?:(?P<test>(?:(?!789)[\s\S])*)456(?P<test1>(?P>test))789))
                  ^^^^^^^^^^^^^^^^^^

请参阅this regex demo