55

我正在维护一个脚本,该脚本可以从各种来源获取输入,并按行处理。根据使用的实际源,换行符可能是 Unix 风格、Windows 风格,甚至对于某些聚合输入,是混合(!)。

从文件中读取时,它是这样的:

@lines = <IN>;
process(\@lines);

...

sub process {
    @lines = shift;
    foreach my $line (@{$lines}) {
        chomp $line;
        #Handle line by line
    }
}

所以,我需要做的是将 chomp 替换为删除 Unix 风格或 Windows 风格的换行符的东西。我想出了太多的方法来解决这个问题,这是 Perl 的常见缺点之一 :)

您对消除通用换行符的最佳方法有何看法?什么是最有效的?

编辑:一个小的澄清 - 方法'process'从某处获取行列表,而不是从文件中读取。每行可能有

  • 没有尾随换行符
  • Unix 风格的换行符
  • Windows 风格的换行符
  • 只是回车(当原始数据具有 Windows 样式的换行符并使用 $/ = '\n' 读取时)
  • 线条具有不同样式的聚合集
4

7 回答 7

95

在对perlre文档进行了一些挖掘之后,我将提出迄今为止我最好的建议,它似乎工作得很好。Perl 5.10 添加了 \R 字符类作为通用换行符:

$line =~ s/\R//g;

它与以下内容相同:

(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])

我会暂时保留这个问题,看看是否有更多漂亮的方法等待被建议。

于 2009-05-19T11:14:17.187 回答
13

每当我通过输入并想要删除或替换字符时,我都会通过像这样的小子程序运行它。

sub clean {

    my $text = shift;

    $text =~ s/\n//g;
    $text =~ s/\r//g;

    return $text;
}

这可能并不花哨,但这种方法多年来对我来说一直完美无缺。

于 2011-02-18T22:46:05.063 回答
8
$line =~ s/[\r\n]+//g;
于 2009-05-19T09:41:59.433 回答
7

阅读perlport我建议像

$line =~ s/\015?\012?$//;

对于您使用的任何平台以及您可能正在处理的任何换行样式都是安全的,因为 \r 和 \n 中的内容可能因不同的 Perl 风格而异。

于 2009-05-19T10:37:45.250 回答
6

2017 年的注释:由于设计错误和未维护的错误,不推荐使用 File::Slurp。请改用File::SlurperPath::Tiny

延伸你的答案

use File::Slurp ();
my $value = File::Slurp::slurp($filename);
$value =~ s/\R*//g;

File::Slurp 抽象出 File IO 的东西,只为你返回一个字符串。

笔记

  1. 重要的是要注意添加/g,没有它,给定一个多行字符串,它只会替换第一个违规字符。

  2. 此外,删除$对于此目的是多余的,因为我们想要删除所有换行符,而不仅仅是$在此操作系统上的任何含义之前的换行符。

  3. 在多行字符串中,$匹配字符串的结尾这将是有问题的)。

  4. 第 3 点意味着第 2 点是在假设您还想使用的前提下做出/m$并设法找到\R* 继续进行的$

例子

while( my $line = <$foo> ){
      $line =~ $regex;
}

鉴于上述符号,操作系统不理解您的文件 '\n' 或 '\r' 分隔符,在默认情况下,操作系统的默认分隔符设置为$/将导致将整个文件作为一个连续的字符串读取(除非您的字符串中有 $OS 的分隔符,它将由它分隔)

所以在这种情况下,所有这些正则表达式都是无用的:

  • /\R*$// :只会擦除\R文件中的最后一个序列
  • /\R*// :只会擦除\R文件中的第一个序列
  • /\012?\015?//: When 只会擦除第一个012\015, \012, or\015序列,\015\012将导致要么 要么\012\015发射。

  • /\R*$//: 如果文件中恰好没有 '\015$OSDELIMITER' 的字节序列,那么除了操作系统自己的换行符之外,不会删除任何换行符。

似乎没有人明白我在说什么,所以这里是示例代码,经过测试不会删除换行符。运行它,你会看到它留下了换行符。

#!/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 Unprocessed输出,请参见此处:http ://pastebin.com/f2c063d74

请注意,某些组合当然有效,但它们很可能是您自己天真地测试过的组合。

请注意,在此输出中,所有结果必须>|$string|<>|$string|< 采用NO LINE FEEDS的形式才能被视为有效输出。

并且$string是一般形式{0}$data{1}$delimiter{2},在所有输出源中,应该有:

  1. {1}和之间没有{2}
  2. |<>|{1}和之间{2}
于 2009-05-19T17:35:03.150 回答
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
于 2014-05-28T15:31:24.183 回答
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 'greedy' 修饰符,因此它会继续查找双空格,直到将它们全部替换。(有效地替换单个空格以外的任何内容)

于 2016-08-19T13:40:54.533 回答