输出每次匹配的捕获组

人工智能 2026-07-09

这两种方法都能用:

echo 'jg984tj89g02jg09248gj4209jg49' | perl -0lne 'print "$_\n" for /(\d)([a-z])/gs'
echo 'jg984tj89g02jg09248gj4209jg49' | perl -0lne '@m = /(\d)([a-z])/gs; print for @m'

但这一个不行:

echo 'jg984tj89g02jg09248gj4209jg49' | perl -0lne 'print "$1-->$2\n" for /(\d)([a-z])/gs'

(基本上,最后一个命令会把最后一次匹配的捕获组重复输出,而不是对每个匹配逐一迭代。)

为什么?

如果能够用最后一个命令来控制输出格式就好了。那本来可以是一种快速、简单且方便的方式,用来为任何后续命令准备/整理数据。

解决方案

for 替换为 while

... | perl -Mv5.14 -wne'say "$1-->$2" while /(\d)([a-z])/g'

使用 for 循环时,正则表达式在列表上下文中计算,因此行为会截然不同。


具体来说,这是关于 /g 修饰符在列表上下文与标量上下文中的行为。来源见 [perlop]:

/g 修饰符指定全局模式匹配——也就是说,在字符串中尽可能多地匹配。它的行为取决于上下文。 在列表上下文中,它返回正则表达式中任意捕获括号所匹配的子串列表。若没有括号,它将返回所有匹配的字符串列表,就好像整个模式被括起来一样。

在标量上下文中,m//g 的每一次执行都会找到下一个匹配,若匹配则返回真,否则返回假。最后一个匹配后的位置可以使用 pos() 函数读取或设置;详见perlfunc中的“pos”。一个失败的匹配通常会将搜索位置重置到字符串开头,但通过添加 /c 修饰符可以避免这种情况(例如 m//gc)。修改目标字符串也会重置搜索位置。

for 循环的列表中,正则表达式处于列表上下文,我们会得到所有捕获 —— $1$2 被设置为最后两个,因此在每次 for 循环迭代中,我们都会得到同样的那两个。 但 while 循环为其条件设置了标量上下文,然后 /g 在字符串中迭代匹配,因此每次循环时我们都会得到指定的两组捕获中的下一对,并且相应的 $1$2 也会随之改变。


  • -Mv5.14 的存在是为了 say,因为简单的 -E 对未来来说并不好。多亏ikegami
  • 那个 \d 是基于Unicode且支持区域设定的,它匹配任何具有 \p{Digit} 属性的字符。大概有三百多种字符,来自各种书写系统;在遇到Unicode时,这可能会让人产生误导,甚至带来风险。

可以通过 /a 修饰符将其限制为ASCII,但如果你想要ASCII,为什么不直接使用 [0-9] 呢?

请参阅 perlrecharclass,以及这个 Stack Overflow页面 的示例。此外,你也可以向AI请求示例,例如:Perl显示正则表达式中 \d匹配的Unicode字符类型的示例

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章