输出每次匹配的捕获组
这两种方法都能用:
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字符类型的示例