我正在维护一个可以从各种来源获取输入的脚本,并且每行都可以使用它。根据所使用的实际来源,换行符可能是Unix风格,Windows风格甚至是某些聚合输入,混合(!)。
从文件中读取时,它会是这样的:
@lines = <IN>;
process(\@lines);
...
sub process {
@lines = shift;
foreach my $line (@{$lines}) {
chomp $line;
#Handle line by line
}
}
所以,我需要做的是将chomp替换为删除Unix风格或Windows风格的换行符。 我提出了解决这个问题的方法太多,这是Perl常见的缺点之一:)
您对阻止通用换行符的最佳方式有什么看法?什么是最有效的?
编辑:一个小小的澄清 - 方法'进程'从某个地方获取行列表,不能从文件中读取。每行可能有
答案 0 :(得分:86)
稍微深入了解perlre文档之后,我将提出我迄今为止最好的建议,看起来效果还不错。 Perl 5.10将\ R字符类添加为通用换行符:
$line =~ s/\R//g;
它与:
相同(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])
我会暂时搁置这个问题,只是为了看看是否有更好的方式等待建议。
答案 1 :(得分:12)
每当我查看输入并希望删除或替换字符时,我都会通过像这样的小子程序运行它。
sub clean {
my $text = shift;
$text =~ s/\n//g;
$text =~ s/\r//g;
return $text;
}
这可能不是很花哨,但这种方法多年来一直对我无瑕疵。
答案 2 :(得分:7)
阅读perlport我建议像
$line =~ s/\015?\012?$//;
对于您所使用的任何平台以及您可能正在处理的任何换行样式都是安全的,因为\ r和\ n中的内容可能因不同的Perl风格而有所不同。
答案 3 :(得分:6)
$line =~ s/[\r\n]+//g;
答案 4 :(得分:6)
2017年注意:由于设计错误和未维护的错误,建议不要使用File :: Slurp。请改用File::Slurper或Path::Tiny。
延伸你的答案
use File::Slurp ();
my $value = File::Slurp::slurp($filename);
$value =~ s/\R*//g;
File :: Slurp抽象出File IO的东西,只为你返回一个字符串。
注意强>
重要的是要注意添加/g
,如果没有它,给定一个多行字符串,它只会替换第一个违规字符。
此外,删除$
,这对于此目的而言是多余的,因为我们要删除所有换行符,而不仅仅是换行符之前的换行符此操作系统上的$
。
在多行字符串中,$
匹配字符串的结尾,这会有问题)。
第3点意味着第2点是假设您还想使用/m
,否则'$'对于包含&gt; 1行的字符串中的任何实际内容都基本无意义或者,进行单行处理,实际了解$
并设法找到继续\R*
$
的操作系统
醇>
示例
while( my $line = <$foo> ){
$line =~ $regex;
}
鉴于上述注释,操作系统无法理解您的文件'\ n'或'\ r'分隔符,在默认情况下,操作系统的默认分隔符设置为$/
将导致读取整个file作为一个连续的字符串(除非你的字符串中包含$ OS的分隔符,它将在那里划分)
所以在这种情况下,所有这些正则表达式都是无用的:
/\R*$//
:只会删除文件\R
的最后一个序列
/\R*//
:只会删除文件\R
的第一个序列
/\012?\015?//
:当只删除第一个012\015
,\012
或\015
序列时,\015\012
将导致{{1} }}或\012
被发出。
\015
:如果文件中没有'\ 015 $ OSDELIMITER'的字节序列,那么除了操作系统自己的 NO 换行符之外的。
似乎没有人得到我正在谈论的内容,所以这里是示例代码,即测试到 NOT 删除换行符。运行它,你会看到它留下了换行符。
/\R*$//
对于 CLEARLY 未处理的输出,请参阅此处:http://pastebin.com/f2c063d74
请注意,某些组合当然有效,但它们很可能是您自己经过测试的组合。
请注意,在此输出中,所有结果必须采用#!/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 };
}
}
形式, NO LINE FEEDS 才能被视为有效输出。
和>|$string|<>|$string|<
的格式为$string
,在所有输出源中,应该有:
{0}$data{1}$delimiter{2}
和{1}
{2}
和|<>|
{1}
醇>
答案 5 :(得分:2)
在你的例子中,你可以去:
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
答案 6 :(得分:1)
扩展Ted Cambron上面的回答以及这里没有解决的问题:如果你从一大块输入的文本中不加选择地删除所有换行符,你最终会遇到相互碰撞的段落稍后输出该文本时的空格。这就是我使用的:
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;
}
最后一次替换使用g&#39;贪心&#39;修饰符,所以它继续找到双空格,直到它全部替换它们。 (有效地替代任何更多的单一空间)