PAYATHON 2026

Perl 正则表达式何时需要 (?(DEFINE)...)?

数据小许

结论

题目中的两个正则表达式分别对给定字符串执行时,会得到相同结果。但这两种结构不能普遍互换:(?(DEFINE)...) 跳过定义区后继续匹配,(*ACCEPT) 则立即使当前匹配成功结束。当后面还有约束,或模式被嵌入其他正则表达式时,这个区别就会影响结果。

(?(DEFINE)...) 用于集中存放可调用的子模式,并非所有子模式调用都必须使用这种语法。

为什么原例结果相同?

原例的主体是:

^ (\w*?) (?&SUB) (\w*)

(?&SUB) 调用 SUB,其中的 (?1) 再调用第一组的模式 \w*?。这里的 (?1) 是子模式调用,不是匹配第一组已有内容的反向引用。

主体匹配完成后,两种写法的执行路径不同:

  • 遇到 (*ACCEPT),直接成功结束,后面的 SUB 定义不会顺序执行。
  • 遇到 (?(DEFINE)...),跳过其内部定义,然后正常到达模式末尾。

原例后面没有其他匹配要求,因此根据这两种结构的语义,两条路径会产生相同结果。有关定义见 Perl 正则表达式文档。

哪种情况会出现差异?

将模式编译为 qr//,再添加后缀,就能看出区别:

my $early = qr{
    (?&SUB)
    (*ACCEPT)
    (?<SUB>a)
}x;

my $defined = qr{
    (?&SUB)
    (?(DEFINE)(?<SUB>a))
}x;

my $s = 'ac';

my $first  = $s =~ /\A $early   b \z/x;
my $second = $s =~ /\A $defined b \z/x;

按照这两个模式的语义,$first 为真,$second 为假:

  • 第一种匹配到 a 后执行 (*ACCEPT),外围的 b 和 \z 都被跳过。
  • 第二种跳过定义区后继续检查 b,但字符串接下来是 c,因此失败。

普通的 qr// 插值和分组不会将 (*ACCEPT) 的作用限制在该片段内。在递归调用或 (??{}) 动态生成的嵌套模式中,它只结束最内层匹配,因此分析其作用范围时需要结合调用方式。参见 (*ACCEPT) 文档。

如果模式之后还要添加边界、后缀或其他检查,使用 (?(DEFINE)...) 可以保留正常的顺序匹配流程。

捕获组和列表上下文需要注意什么?

(?(DEFINE)...) 本身不创建捕获组,但内部的 (?<SUB>...) 和 (D) 都会占用捕获编号。它们在子模式调用期间匹配到的捕获内容,在调用返回后不会作为这些组的最终捕获值保留。perlre 对此有明确说明。

因此,原例有四个捕获组。在不带 /g 的列表上下文匹配中,后两个位置仍会存在,结果为:

("before", "after", undef, undef)

文档建议将定义区放在末尾,这样可以让业务捕获组排在前面,但末尾的 undef 仍会存在。列表上下文并无此语法要求。定义区也会影响后续捕获组的编号,拼接模式时尤其要留意数字子模式调用。

原问题中的 @{^CAPTURE) 有括号笔误,正确写法是 @{^CAPTURE}。它与列表上下文的匹配返回值应分开讨论。

备注:内容仅供参考。