Perl使用正则分割字符串时捕获组$2重复打印两次的问题
Perl正则替换捕获组内容重复输出问题
问题复现
使用如下Perl代码处理字符串:
#!/bin/env perl use warnings; $line = 'GFHFHDSH4567 FHGFF 687686'; print $line, "\n"; # 先把多个连续空格替换为单个空格 $line =~ s/ +/ /g; # 尝试拆分字符串为前缀和末尾数字两部分 $line =~ s/^(.+)(?=\s+(\d+)$)/1:$1\t2:$2/g; print $line, "\n";
实际运行输出:
GFHFHDSH4567 FHGFF 687686 1:GFHFHDSH4567 FHGFF 2:687686 687686
末尾的数字串687686被重复打印了两次。
问题成因
- 核心错误是对正向零宽断言(正向预查)
(?=pattern)的特性理解有误:零宽断言仅做规则校验,确认当前位置后方的内容符合匹配规则,但不会消耗对应长度的字符串——也就是说断言括号内匹配到的内容,不会被纳入s///操作中需要被替换掉的匹配段,替换完成后这部分内容会原样保留在原位置。 - 拆解你写的替换正则:真正被匹配、会被替换掉的内容只有
^(.+)匹配到的前缀段GFHFHDSH4567 FHGFF;后面的\s+(\d+)$(也就是687686)被包裹在零宽断言里,根本没有进入替换匹配范围,替换后会原封不动留在字符串末尾。 - 替换字符串里的
2:$2确实正确捕获到了末尾的数字687686,会被拼接到替换后的前缀位置;再加上没被替换、原样残留的687686,最终就出现了数字重复两次的现象。 - 额外冗余点:正则开头用
^锚定了字符串起始位置,本身最多只会匹配1次,后面加的/g全局匹配修饰符没有实际作用。
修正方法
去掉多余的零宽断言,把需要匹配的空格和末尾数字直接放到正则捕获段中,让这部分内容被正常消耗、替换即可:
# 去掉零宽断言,移除多余的/g修饰符 $line =~ s/^(.+)\s+(\d+)$/1:$1\t2:$2/;
修正后运行可以得到预期输出:
GFHFHDSH4567 FHGFF 687686 1:GFHFHDSH4567 FHGFF 2:687686
内容的提问来源于stack exchange,提问作者user1980099
相关产品推荐
相关产品推荐

