Perl 17:/g 的下一次匹配有位置状态
在字符串 a,b 上写 while ($text =~ /([^,]+)/g),第一次得到 a,第二次得到 b。差别并非正则凭空“记住上一条日志”,而是目标标量的搜索位置在匹配时变化。实测 pos($text) 先为 1、再为 3;下一次找不到时,默认失败会把位置重置为 undef。若混入 /gc,失败后不重置的契约又不同;不能把两种情况笼统说成“/g 永远继续”。
零长度匹配更容易把位置误判成终止条件。实验对 ab 用 /(?=.)/g:STDOUT 的 zero-width=0,1,而不是无穷多个 0;代码另设最多 3 次的护栏,防止未来改动变成挂起。这只验证了该模式和本机 Perl 5.38.2 的有限样本;文档对重复零长度匹配有专门规则,不能由一个 while 循环推论每个含 \G 的模式都安全。
执行 perl examples/perl-language/experiments/16-20-patterns.pl 17,四行输出和空 STDERR、退出码 0 记录在 examples/perl-language/evidence/15-20.md。工具本体目前按 TAB 切字段,不需要为字段解析人为引入 /g 状态。补充实验 course-semantics.pl regex 同时记录默认 /g 与 /gc:g_parts=a@1|b@3 final_pos=undef,说明普通 /g 在最后一次失败后清掉位置;gc_failed=failed before=1 after=1,说明 /gc 失败时保留先前位置。两行由 t/course-semantics.t 分别断言,避免把“继续扫描”和“失败后是否重置”混成同一条规则。
同一个 m//g 还要看调用上下文。my @parts = ('a,b' =~ /([^,]+)/g) 在列表位置一次拿到 a|b;my $ok = ('a,b' =~ /([^,]+)/g) 在标量位置只推进一次并返回真假,捕获值要从 $1 或命名捕获哈希读取。实验输出 g_list=a|b scalar_first=1 named=service:api zero=0|1 同时覆盖列表 /g、标量 /g、命名捕获和零长度推进;这组输出是原定 14 里“scalar/list /g、命名捕获、零长”验收点在实际路线中的映射。
[PATTERN] pos($text) 是目标标量上的游标状态。默认 /g 适合完整消费循环,失败后回到未设置状态;/gc 适合手写词法扫描器,失败后保留位置让代码决定下一步。若不同解析阶段共享同一个目标标量和 /gc,第二阶段要么主动 pos($text) = 0,要么明确从当前位置继续。官方依据是 Perl 5.38.2 perlop 的 Regexp Quote-Like Operators 和 perlfunc 的 pos。
练习:对同一个未修改的字符串先用 /gc 扫描一段,再换另一个模式扫描,可能漏读什么?/gc 匹配失败也可保留 pos,后一次若期望从头读就可能跳过前缀;若直接修改目标字符串,则搜索位置又会重置,不能把两种动作合为同一个结论。参考 5.38.2 pod/perlop.pod(Regexp Quote-Like Operators 的 /g)、pod/perlfunc.pod(pos)、pod/perlre.pod(Repeated Patterns Matching a Zero-length Substring)。









