最新的方法来删除Perl中的换行符
我正在维护一个脚本,可以从各种来源获得input,并在每行处理它。 取决于实际使用的源代码,换行符可能是Unix风格,Windows风格,或者甚至对于某些聚合input,混合(!)。
当从文件中读取它是这样的:
@lines = <IN>; process(\@lines); ... sub process { @lines = shift; foreach my $line (@{$lines}) { chomp $line; #Handle line by line } }
所以,我需要做的是将chompreplace为删除Unix风格或Windows风格的换行符。 我正在想办法解决这个问题,Perl的通常的缺点之一:)
你有什么看法,以扼杀通用的线路突破? 什么是最有效的?
编辑:一个小的澄清 – 方法'进程'从某处获取行的列表, 而不是从文件nessecarily读取 。 每一行可能都有
- 没有追踪的线索
- Unix风格的换行符
- Windows风格的线代码
- 只是回车(当原始数据具有Windows风格的换行符并且用$ / ='\ n'读取时)
- 线条有不同风格的集合集合
通过perlre文档挖掘了一下, 稍后我会提出我的最好的build议,似乎工作很好。 Perl 5.10添加了\ R字符类作为一个广义的换行符:
$line =~ s/\R//g;
这是一样的:
(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])
我会把这个问题持续一段时间,只是为了看看是否有更多漂亮的方法等待build议。
每当我经历input,想要删除或replace字符,我运行它通过像这样的小子程序。
sub clean { my $text = shift; $text =~ s/\n//g; $text =~ s/\r//g; return $text; }
这可能不是幻想,但这种方法多年来对我来说一直是无懈可击的。
阅读perlport我build议类似的东西
$line =~ s/\015?\012?$//;
无论你在哪个平台上,以及你可能正在处理的换行样式,都是安全的,因为\ r和\ n中的内容可能因不同的Perl风格而有所不同。
从2017年注意:File :: Slurp不build议由于devise错误和未保持的错误。 使用File :: Slurper或Path :: Tiny代替。
延伸你的答案
use File::Slurp (); my $value = File::Slurp::slurp($filename); $value =~ s/\R*//g;
File :: Slurp提取文件IO的东西,只是为你返回一个string。
注意
-
重要的是要注意添加
/g
,如果没有它,给定一个多行string,它只会replace第一个冒犯的字符。 -
另外,删除
$
,这对于这个目的来说是多余的,因为我们要删除所有换行符,而不是在这个操作系统上以$
表示之前的换行符。 -
在多行string中,
$
匹配string的末尾,这将是有问题的)。 -
第3点意味着第2点是假设你也想使用
/m
否则“$”对于任何一行实际上没有任何实际意义的行,或者在进行单行处理时,是一个操作系统实际上理解$
并设法find进行$
\R*
例子
while( my $line = <$foo> ){ $line =~ $regex; }
鉴于上述表示法,操作系统不理解您的文件'\ n'或'\ r'分隔符,默认情况下操作系统的默认分隔符设置为$/
将导致读取整个文件作为一个连续的string除非你的string中有$ OS的分隔符,那么它将由此分隔)
所以在这种情况下,所有这些正则expression式都是无用的:
-
/\R*$//
:只会擦除文件中\R
的最后一个序列 -
/\R*//
:只会擦除文件中的第一个\R
序列 -
/\012?\015?//
:何时只会擦除第一个012\015
,\012
或\015
序列,\015\012
会导致\012
或\015
被发射。 -
/\R*$//
:如果在文件中碰巧没有'\ 015 $ OSDELIMITER'的字节序列,那么除了操作系统自己的以外, 没有换行符将被删除。
它似乎没有人得到我在说什么,所以这里是示例代码,这是testing 不删除换行。 运行它,你会发现它留下了换行符。
#!/usr/bin/perl use strict; use warnings; my $fn = 'TestFile.txt'; my $LF = "\012"; my $CR = "\015"; my $UnixNL = $LF; my $DOSNL = $CR . $LF; my $MacNL = $CR; sub generate { my $filename = shift; my $lineDelimiter = shift; open my $fh, '>', $filename; for ( 0 .. 10 ) { print $fh "{0}"; print $fh join "", map { chr( int( rand(26) + 60 ) ) } 0 .. 20; print $fh "{1}"; print $fh $lineDelimiter->(); print $fh "{2}"; } close $fh; } sub parse { my $filename = shift; my $osDelimiter = shift; my $message = shift; print "Parsing $message File $filename : \n"; local $/ = $osDelimiter; open my $fh, '<', $filename; while ( my $line = <$fh> ) { $line =~ s/\R*$//; print ">|" . $line . "|<"; } print "Done.\n\n"; } my @all = ( $DOSNL,$MacNL,$UnixNL); generate 'Windows.txt' , sub { $DOSNL }; generate 'Mac.txt' , sub { $MacNL }; generate 'Unix.txt', sub { $UnixNL }; generate 'Mixed.txt', sub { return @all[ int(rand(2)) ]; }; for my $os ( ["$MacNL", "On Mac"], ["$DOSNL", "On Windows"], ["$UnixNL", "On Unix"]){ for ( qw( Windows Mac Unix Mixed ) ){ parse $_ . ".txt", @{ $os }; } }
对于CLEARLY未处理的输出,请参见: http : //pastebin.com/f2c063d74
请注意,当然有一定的组合,但他们可能是你自己经过testing的。
请注意,在此输出中,所有结果都必须是>|$string|<>|$string|<
, NO LINE FEEDS被视为有效输出。
$string
的forms为{0}$data{1}$delimiter{2}
,其中在所有输出源中,应该是:
-
{1}
和{2}
之间没有任何内容 - 只有
|<>|
在{1}
和{2}
$line =~ s/[\r\n]+//g;
在你的例子中,你可以去:
chomp(@lines);
要么:
$_=join("", @lines); s/[\r\n]+//g;
要么:
@lines = split /[\r\n]+/, join("", @lines);
直接在文件上使用这些:
perl -e '$_=join("",<>); s/[\r\n]+//g; print' <a.txt |less perl -e 'chomp(@a=<>);print @a' <a.txt |less
为了扩展Ted Cambron的回答,以及这里没有提到的东西:如果你从一段input的文本中不加区分地删除所有换行符,那么当你稍后输出文本时,最后会有段落相互之间没有空格。 这是我使用的:
sub cleanLines{ my $text = shift; $text =~ s/\r/ /; #replace \r with space $text =~ s/\n/ /; #replace \n with space $text =~ s/ / /g; #replace double-spaces with single space return $text; }
最后一个replace使用g'贪婪'修饰符,所以它继续find双空格,直到它们全部replace它们。 (有效地替代更多的单一空间)