删除Perl中的换行符的最佳方法

Chr*_*fer 50 perl line-breaks

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

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

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

...

sub process {
    @lines = shift;
    foreach my $line (@{$lines}) {
        chomp $line;
        #Handle line by line
    }
}
Run Code Online (Sandbox Code Playgroud)

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

您对扼杀通用换行符的最佳方式有什么看法?什么是最有效的?

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

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

Chr*_*fer 88

稍微深入了解一下perlre docs后,我将提出我迄今为止最好的建议,看起来效果还不错.Perl 5.10将\ R字符类添加为通用换行符:

$line =~ s/\R//g;
Run Code Online (Sandbox Code Playgroud)

它与以下相同:

(?>\x0D\x0A?|[\x0A-\x0C\x85\x{2028}\x{2029}])
Run Code Online (Sandbox Code Playgroud)

我会把这个问题保持一段时间,只是为了看看是否有更好的方式等待建议.


Ted*_*ron 12

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

sub clean {

    my $text = shift;

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

    return $text;
}
Run Code Online (Sandbox Code Playgroud)

这可能不是很花哨,但这种方法多年来一直对我无瑕疵.


Olf*_*fan 7

阅读perlport我会建议像

$line =~ s/\015?\012?$//;
Run Code Online (Sandbox Code Playgroud)

无论您使用什么平台以及您可能正在处理的任何换行样式都是安全的,因为\ r和\n中的内容可能因不同的Perl风格而有所不同.

  • 1)+2)由于我不知道线条内部的内容我不得不假设内部可能存在不应删除的换行符(例如带有换行数据列的数据库记录).我的意图是尽可能地匹配chomp()的行为.3)我看过旧的Mac只使用\ 015而Windows仍然使用\ 015\012,但我还没有看到使用\ 012\015的真实世界系统,所以我觉得这个顺序是安全的.;) (2认同)
  • 小心!简单地合并两行将连接第X行的最后一个"字"和第X + 1行的"第一个"字.根据您可能不想删除的上下文,但替换为SPACE(或其他分隔符) (2认同)

Ken*_*ric 6

2017年的注意事项:由于设计错误和未维护的错误,不建议使用File :: Slurp.请改用File :: SlurperPath :: Tiny.

延伸你的答案

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

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

注意

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

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

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

  4. 第3点意味着第2点是假设您还想要使用/m否则'$'对于具有> 1行的字符串中的任何实际内容基本上没有意义,或者,执行单行处理,实际上理解的操作系统$并设法找到\R* 继续进行的$

例子

while( my $line = <$foo> ){
      $line =~ $regex;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • /\R*$// :只会删除\R文件中的最后一个序列
  • /\R*// :只会删除\R文件中的第一个序列
  • /\012?\015?//:何时只擦除第一个012\015,\012\015序列,\015\012将导致\012或者\015被发射.

  • /\R*$//:如果碰巧是在文件"\ 015 $ OSDELIMITER"没有字节序列,然后再NO换行符将除操作系统本身的那些去除.

这样看来,没有人得到什么我谈论,所以这里是示例代码,即测试删除换行符.运行它,你会看到它留下了换行符.

#!/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 };
    }
}
Run Code Online (Sandbox Code Playgroud)

有关CLEARLY Unprocessed输出,请参见此处:http://pastebin.com/f2c063d74

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

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

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

  1. {1}和之间没什么{2}
  2. |<>|{1}和之间{2}