$和Perl的全局正则表达式修饰符

6
我终于想出如何将文本添加到文件的每一行末尾了:
perl -pe 's/$/addthis/' myfile.txt

然而,由于我想学习Perl以便频繁使用正则表达式,我无法理解为什么以下Perl命令会在每行的开头和结尾添加文本“addthis”:
perl -pe 's/$/addthis/g' myfile.txt

我认为'$'符号无论在正则匹配中使用什么修饰符都表示匹配行尾,但我猜想这个理解是错误的?

1
你有阅读 $ 的文档吗? - ysth
3个回答

11

摘要: 对于你正在做的事情,去掉/g,这样它只匹配换行符前的内容。 /g 告诉它在换行符前和字符串结尾(换行符后)都要匹配。

没有/m修饰符,$将匹配换行符前(如果出现在字符串末尾)或字符串末尾之前的位置。例如,在"foo""foo\n"中,$将在foo之后匹配。但是,在"foo\nbar"中,它将在bar之后匹配,因为嵌入的换行符不在字符串末尾。

使用/g修饰符,您将获取所有$将匹配的位置 -- 因此

s/$/X/g;

会将类似于"foo\n"的一行转换为"fooX\nX"

侧边栏: /m修饰符将允许$匹配出现在字符串末尾之前的换行符。

s/$/X/mg;

"foo\nbar\n"转换为"fooX\nbarX\nX"


5

正如吉姆·戴维斯指出的那样,$匹配字符串的末尾或者在/m选项下匹配\n字符之前。(请参见正则表达式部分和perlre Perldoc页面。使用g修饰符可以使其继续匹配。)

多行Perl正则表达式(即,即使只在行末出现一次,也包含换行符的Perl正则表达式)会导致各种复杂问题,大多数Perl程序员都难以处理。

  • 如果您逐行读取文件,请在操作该行之前始终使用 chomp。这将解决您使用 g 限定符时遇到的问题。

  • 如果您在 Linux/Mac 上读取来自 Windows 的文件,则可能会出现其他问题。在这种情况下,您将同时拥有 \r\n 字符。最近我尝试调试程序时发现,\r 字符不会被 chomp 移除。因此,我现在总是确保以文本模式打开文件进行读取。

像这样:

open my $file_handle, "<:crlf", $file...

如果在Linux / Mac系统上使用的是Windows文件,则此功能将自动将\r\n字符替换为\n。 如果这是一个常规的Linux / Mac文本文件,则它将不起作用。 另一个明显的解决方案是不使用Windows(即使是个玩笑也可以!)。

当然,在您的情况下,先使用chomp会执行以下操作:

$cat file
line one
line two
line three
line four
$ perl -pe 'chomp;s/$/addthis::/g`
line oneaddthis::line twoaddthis::line threeaddthis::line fouraddthis::

chomp 命令去掉了 \n,所以现在当行被打印出来时,你看不到它。嗯...
$ perl -pe 'chomp;s/$/addthis/g;print "\n";
line oneaddthis
line twoaddthis
line threeaddthis
line fouraddthis

那很有效!而且,你的一句话只是稍微有点难以理解。
另一件事是采用 Damian Conway 在他的书 Perl Best Practices 的第12章中推荐的更现代的方法:

使用 \A\z 作为字符串边界锚点。

即使您不采用始终使用 /m 的先前做法,使用具有默认含义的 ^ 和 $ 是一个坏主意。当然,您知道在 Perl 正则表达式中 ^ 和 $ 实际上意味着什么。但是阅读或维护代码的人会知道吗?还是他们更可能以前述方式错误地解释这些元字符? Perl 提供了标记,它们始终且明确地表示“字符串的开头”和“字符串的结尾”:\A 和 \z(大写 A,但小写 z)。无论 /m 是否激活,它们都表示“字符串的开头/结尾”。无论读者认为 ^ 和 $ 意味着什么,它们都表示“字符串的开头/结尾”。

如果您遵循 Conaway 的建议,并执行以下操作:

perl -pe 's/\z/addthis/mg' myfile.txt

你会发现你的短语addthis只被添加到每行的末尾:
$cat file
line one
line two
line three
line four
$ perl -pe `s/\z/addthis/mg` myfile.txt
line one
addthisline two
addthisline three
addthisline four
addthis

看看它的效果有多好。那个addthis被添加到每行的末尾!...紧接着该行的\n字符后面。
够玩了,回到工作中。(等等,今天是总统节。这是一个带薪假期。除了当然所有那些我承诺在星期二早上完成的事情之外,今天不用工作)。
希望这能帮助你理解正则表达式有多有趣,以及为什么很多人决定学习Python。

1. 了解在Perl中^$的真正含义吗?嗯,是的,当然知道。我已经用Perl编程几十年了。是的,我知道所有这些。 (自己注意:$显然不是我一直以为的意思。)


谢谢您提供这么有趣且富有启发性的答案!我本来以为我知道'$'的意思... <虚心低头> 这个年轻的蚂蚱还有很多要学习 :) - drapkin11

0
一个解决方法:
perl -pe 's/\n/addthis\n/' 

无需 g 修饰符:正则表达式逐行处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接