Perl正则匹配后@-与@+数组长度不同的原因及验证问题
Perl正则中@-与@+数组长度差异的原因及验证代码有效性分析
问题背景
原本预期Perl正则匹配成功后,@-和@+数组长度应当一致,但实际测试发现两者存在差异。示例脚本及运行结果如下:
示例脚本
#!/usr/bin/perl -w use strict; use warnings; use v5.20.0; use Data::Dumper; $Data::Dumper::Terse = 1; my $rex = '([a-z]+) | ([0-9]+) | ([A-Z]+)'; my $str = '9999'; if ( $str =~ m/$rex/x ) { say Dumper(\@{^CAPTURE}); say Dumper(\@-); say Dumper(\@+); }
运行输出
[ undef, '9999' ] [ 0, undef, 0 ] [ 4, undef, 4, undef ]
从结果可见:@-不会为末尾未被尝试匹配的分组添加undef元素,而@+会包含所有定义过的捕获组对应的位置(未匹配则为undef)。
一、@-与@+长度差异的原因
Perl中@-和@+数组的设计目标不同,导致了这种长度差异:
@-数组记录的是实际参与过匹配尝试的分组的起始位置:索引0对应整个匹配$&的起始位置,后续索引依次对应$1、$2...这些被引擎尝试匹配过的分组的起始位置。如果某个捕获组所在的分支根本没被执行(比如示例中第三个([A-Z]+)分支,因为第二个分支已匹配成功,引擎不会再尝试第三个分支),该分组不会被加入@-,因此数组长度不会包含这类未尝试的分组。@+数组的设计是严格对应正则表达式中所有定义的捕获组:索引0对应整个匹配的结束位置,后续索引依次对应每一个捕获组(不管是否被尝试匹配)的结束位置。对于未被尝试或匹配失败的分组,对应位置填充undef,因此数组长度等于捕获组总数+1。
这种差异是Perl正则引擎的历史实现逻辑,@-聚焦于实际发生的匹配行为,@+则覆盖所有定义的捕获组,满足开发者对所有分组位置的查询需求。
二、验证代码的通用性分析
针对验证代码:
if ( $str =~ m/$rex/x ) { scalar(@{[$&, @{^CAPTURE}]}) == scalar(@-) or die; }
该代码的逻辑是:合并$&(整个匹配内容)与@{^CAPTURE}(所有被尝试过的捕获组结果,未匹配则为undef)的数组长度,与@-的长度做对比。
这个验证逻辑适用于所有匹配成功的场景:
@{^CAPTURE}的元素数量等于所有被引擎尝试过的捕获组的数量(无论匹配成功与否);- 加上
$&后,数组总长度为1 + 被尝试的捕获组数量,正好等于@-的长度(@-索引0对应$&的起始,后续每个索引对应一个被尝试的捕获组)。
因此只要正则匹配成功,该验证逻辑都成立。
内容的提问来源于stack exchange,提问作者Britton Kerin
相关产品推荐
相关产品推荐

