PCRE正则表达式使用命名模式子程序

5
我正在尝试使用PHP PCRE中的命名子模式/'子程序'正则表达式功能,希望有人可以解释以下奇怪的输出:
$re = "/
(?(DEFINE)
    (?<a> a )
)

^(?&a)$

/x";

var_dump(preg_match($re, 'a', $match)); // (int) 1 as expected
var_dump($match); // Array( [0] => 'a' ) <-- Why?

我不明白为什么命名组"a"的内容没有出现在结果中。将preg_match更改为preg_match_all可以将"a"和"1"放入匹配数据中,但两者都只包含空字符串。
我真的很喜欢用这种方式编写正则表达式,因为你可以使它们非常强大,同时保持可维护性(请参见此答案,其中有一个很好的例子),但是如果匹配数据中没有子模式,则实际上没有什么用处。
我是否遗漏了什么或者我应该只是悲叹可能已经失去的东西并继续前进?
1个回答

5
这些子模式不捕获组是很有道理的 - 它们的主要目的是被多次使用,因此你不能真正地捕获它们。此外,如果默认情况下捕获所有子模式,就无法选择在不想要的位置不捕获组 - 这不是最好的默认行为。相反,通过在 (?&a) 语句周围添加另一个组即可进行捕获。
我在 PCRE.org 上找不到相关参考。最接近的是这个,它与直接匹配 (?<a>...) 不相关(尽管您可能期望为空组):

在子程序调用期间设置的任何捕获括号都会在之后恢复为其先前的值。

Perl 手册 上更清晰(相关部分已突出显示):

An example of how this might be used is as follows:

/(?<NAME>(?&NAME_PAT))(?<ADDR>(?&ADDRESS_PAT))
(?(DEFINE)
(?<NAME_PAT>....)
(?<ADRESS_PAT>....)
)/x

Note that capture buffers matched inside of recursion are not accessible after the recursion returns, so the extra layer of capturing buffers is necessary.


1
没错,这就是 (?(DEFINE)…) 的工作原理。它类似于声明子程序。我有时会在定义之外使用全大写的命名捕获组,并在其中使用小写的可调用非捕获组,以帮助我区分两者。请查看此答案中给出的较长解决方案,了解我如何同时使用命名组进行调用和捕获。我从 %+ 哈希表中提取捕获的内容,例如 $+{VALUE}@⁠+⁠{ qw< TAG BODY > } - tchrist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接