Perl 16:只在本次匹配成功的分支使用捕获
消息 id=abc-01 能拆成 id 和 abc-01;消息 id= 不能变成一条没有值却被接受的记录。正则既要覆盖整个字段,又必须让后续代码只在匹配成功的分支使用捕获结果。用一个成功匹配后的 $1 再去检查失败匹配,是把上一次状态误当成这一次数据。
实验 perl examples/perl-language/experiments/16-20-patterns.pl 16 运行锚定在 \A、\z 的 (?<key>[a-z]+)=(?<value>[a-z0-9-]+):STDOUT 是 accepted:id:abc-01 与 rejected:id= 两行,STDERR 空,退出码 0。代码仅在 if ($message =~ /.../) 成功时读取 $+{key}、$+{value}。这是一段消息子语法的实验,不是说整行 TSV 都该换成一条大正则。日志字段数、时间和等级仍遵守之前的解析契约。
图:消息子语法的匹配与捕获分支
1 | |
补充实验 course-semantics.pl regex 直接保留这个陷阱:先成功匹配 id=abc-01,再让 id= 匹配失败,输出仍是 failed=failed old_capture=abc-01。这符合 Perl 5.38.2 perlre 的规则:失败匹配不会重置捕获变量,捕获状态会持续到块结束或下一次成功匹配。因此标题和代码都必须强调“成功分支内使用”,而不是把 $1、$+{name} 当作失败后自动清空的临时槽。
[PATTERN] 捕获变量是上一场成功匹配留下的状态,不是当前输入的结构化字段。安全写法是把读取捕获放进 if ($text =~ /.../) { ... } 内部,或在成功后立即复制到词法变量;失败分支只处理拒绝原因,不读取 $1 推断当前输入。t/course-semantics.t 与原 t/15-20-query-and-patterns.t 分别覆盖旧捕获保留和锚定子语法。
练习:只把正则的开头锚点删掉,prefix id=abc-01 能否按本课约定算合法?不能;既然业务约定整个消息是一个键值对,前缀必须被拒绝。这里的字符类也是有意限制:不是所有 Unicode 标识符都允许进入该子格式。正反样本、完整输出和断言在 examples/perl-language/evidence/15-20.md;参考 Perl 5.38.2 pod/perlre.pod(The Basics、Assertions、Named Capture Groups)。









