6

由于文件有 CRLF 行结尾,我在 perl oneliner 失败时挠了挠头一小时。它在行尾有一个带有组匹配的正则表达式,并且 CR 包含在匹配中,使用反向引用进行替换会造成不好的东西。

我最终在正则表达式中手动指定了 CRLF,但是有没有办法让 perl 句柄自动换行

原始命令是

perl -pe  's/foo bar(.*)$/foo $1 bar/g' file.txt

“正确”命令是

perl -pe  's/foo bar(.*)\r\n/foo $1 bar\r\n/g' file.txt

我知道我也可以在处理之前转换行尾,我对如何让 Perl 优雅地处理这种情况很感兴趣。

示例文件(以 CRLF 行结尾保存!)

[19:06:57.033] foo barmy
[19:06:57.033] foo baryour

预期产出

[19:06:57.033] foo my bar
[19:06:57.033] foo your bar

使用原始命令输出(bar 位于行首,因为它与回车符匹配):

bar:06:57.033] foo my
bar:06:57.033] foo your
4

5 回答 5

6

首先,让我们记住

perl -ple's/foo bar(.*)\z/foo $1 bar/g' file.txt

是接近的东西的缩写

perl -e'
   while (<>) {
      chomp;
      s/foo bar(.*)\z/foo $1 bar/g;
      print $_, $/;
   }
' file.txt

Perl 使得代码可以以独立于平台的方式读取/写入本地文本文件。

在评论中,您询问了如何以独立于平台的方式读取/写入本地文本文件和外部文本文件。

首先,您必须禁用 Perl 的正常处理。

binmode STDIN;
binmode STDOUT;

然后你必须处理多行结尾。

sub mychomp { (@_ ? $_[0] : $_) =~ s/(\s*)\z//; $1 }

while (<STDIN>) {
   my $le = mychomp($_);
   s/foo bar(.*)\z/foo $1 bar/g;
   print($_, $le);
}

所以而不是

perl -ple's/foo bar(.*)\z/foo $1 bar/g' file.txt

你将会拥有

perl -e'
   sub mychomp { (@_ ? $_[0] : $_) =~ s/(\s*)\z//; $1 }

   binmode STDIN;
   binmode STDOUT;
   while (<STDIN>) {
      my $le = mychomp($_);
      s/foo bar(.*)\z/foo $1 bar/g;
      print($_, $le);
   }
' <file
于 2013-10-30T14:03:27.963 回答
4

在较新的 perls 中,您可以\R在您的正则表达式中使用来去除所有行尾字符(它包括\n\r)。请参阅perldoc perlre

于 2013-10-30T23:11:33.357 回答
1

你可以说:

perl -pe 's/foo bar([^\015]*)(\015?\012)/foo $1 bar$2/g' *.txt

行尾将被保留,即与输入文件相同。


您可能还想参考perldoc perlport.

于 2013-10-30T12:56:59.937 回答
1

有没有办法让 perl 自动处理特定于平台的行尾?

是的。它实际上是默认值。

问题是您正在尝试在 unix 平台上处理 Windows 行尾。

这肯定会做到:

perl -pe'
    BEGIN {
       binmode STDIN,  ":crlf";
       binmode STDOUT, ":crlf";
    }
    s/foo bar(.*)$/foo $1 bar/g;
' <file.txt

我可以建议您继续手动操作吗?

或者,您可以将文件转换为文本文件并将其转换回来。

<file.orig dos2unix | perl -pe'...' | unix2dos >file.new
于 2013-10-30T13:17:42.927 回答
1

\R转义序列 Perl v5.10 +perldoc rebackslash请参阅在线文档,匹配“通用换行符”(与平台无关) 可以在此处工作(示例使用 Bash 创建多行输入字符串):

$ printf 'foo barmy\r\nfoo baryour\r\n' | perl -pe 's/foo bar(.*?)\R/foo $1 bar\n/gm'
foo my bar
foo your bar

请注意,与Ether 答案的唯一区别是使用了非贪婪构造.*?而不仅仅是.*),这在此处产生了所有不同。

如果您想了解更多信息,请继续阅读。


背景:

这是一个与相关的陷阱的例子\R,它源于它可以匹配一个或两个字符-\r\n或者,通常是\n[1]

使用贪婪(.*)构造 ,"my\r"-包括- 被捕获,因为正则\r表达式引擎显然只回溯一个要查找的字符\R,其余\n 的本身也满足。

相比之下,使用非贪婪(.*?)构造会按预期\R匹配\r\n 序列

[1]\R匹配 MORE 不仅仅是\r\n\n:它匹配在 Unicode 术语中被归类为垂直空白\v的任何单个字符,其中还包括(垂直制表符)、\f(换页)、(单独\r)和以下 Unicode 字符:0x133 (NEXT LINE)、、、和0x2028 (LINE SEPARATOR)0x8232 (LINE SEPARATOR)0x8233 (PARAGRAPH SEPARATOR)

于 2015-09-30T23:19:48.240 回答