web-dev-qa-db-ja.com

Perlで改行を削除する最新の方法

さまざまなソースから入力を取得し、1行ごとに動作するスクリプトを管理しています。使用される実際のソースに応じて、改行はUnixスタイル、Windowsスタイル、または集約された入力の場合はmixed(!)になります。

ファイルから読み取る場合、次のようになります。

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

...

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

だから、私がする必要があるのは、ムシャムシャを、UnixスタイルまたはWindowsスタイルの改行を削除するものに置き換えることです。私はこれを解決するあまりにも多くの方法を考え出していますが、Perlの通常の欠点の1つです:)

一般的な改行を削除する最も近い方法についてはどう思いますか?最も効率的なものは何でしょうか?

編集:小さな説明-メソッド 'process'はどこかから行のリストを取得しますファイルから誤って読み込まれない。各行には

  • 末尾の改行なし
  • Unixスタイルの改行
  • Windowsスタイルの改行
  • キャリッジリターンのみ(元のデータにWindowsスタイルの改行があり、$/= '\ n'で読み取られる場合)
  • 線のスタイルが異なる集合セット
50
Christoffer

perlre のドキュメントを少し掘り下げた後、これまでのところ、かなりうまくいくと思われる最善の提案を提示します。 Perl 5.10は、一般化された改行として\ R文字クラスを追加しました。

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

以下と同じです:

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

まだ提案されていない気の利いた方法があるかどうかを確認するために、この質問をしばらく開いたままにします。

88
Christoffer

入力を行って文字を削除または置換するたびに、このような小さなサブルーチンを実行します。

sub clean {

    my $text = shift;

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

    return $text;
}

それは空想ではないかもしれませんが、この方法は何年も私にとって完璧に機能していました。

12
Ted Cambron

perlport を読む

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

\ rと\ nの内容はPerlのフレーバーによって異なる可能性があるため、現在使用しているプラ​​ットフォームや処理するラインフィードスタイルに対して安全であるようにします。

7
Olfan

2017年からの注意:File :: Slurpは、設計ミスやメンテナンスされていないエラーのため推奨されません。代わりに File :: Slurper または Path :: Tiny を使用してください。

あなたの答えを広げる

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

File :: SlurpはFile IO stuffを抽象化し、文字列を返します。

[〜#〜] note [〜#〜]

  1. /gの追加に注意することは重要です。これがない場合、複数行の文字列が与えられた場合、firstの問題のある文字のみを置き換えます。

  2. また、このOSで$が意味するものの前の改行だけでなくall改行を削除するため、この目的のために冗長な$の削除。

  3. 複数行の文字列では、$stringの末尾に一致しますが、これは問題となります)。

  4. ポイント3は、ポイント2が/mも使用するという前提で作成されることを意味します。そうしないと、「$」は、1行を超える文字列、または単一行処理を行う、実際にはOS $を理解し、\R*を進める$を見つけることに成功する

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

上記の表記法を考えると、OSのデフォルトの区切り文字が$/に設定されているデフォルトのシナリオで、ファイルの区切り文字「\ n」または「\ r」を理解しないOSは、ファイル全体を1つの連続した文字列として読み取ります(ただし、文字列には$ OSの区切り文字が含まれており、その区切り文字で区切られます)

したがって、この場合、これらの正規表現はすべて役に立ちません。

  • /\R*$//:ファイル内の\Rの最後のシーケンスのみを消去します
  • /\R*//:ファイル内の\Rの最初のシーケンスのみを消去します
  • /\012?\015?//:最初の012\015\012、または\015シーケンスのみを消去する場合、\015\012\012または\015のいずれかが発行されます。

  • /\R*$//:ファイルに '\ 015 $ OSDELIMITER'のバイトシーケンスが存在しない場合、[〜#〜] no [〜#〜] OS以外の改行は削除されますもの。

私が話していることは誰にもわからないように見えるので、ここにサンプルコード、つまりtested to [〜#〜] not [〜#〜]を削除します。実行すると、ラインフィードが残っていることがわかります。

#!/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

もちろん機能する特定の組み合わせがありますが、それらはおそらくあなた自身がテストしたものです。

この出力では、有効な出力と見なされるために、すべての結果はNO LINE FEEDSを含む>|$string|<>|$string|<の形式でなければなりません。

$stringは一般的な形式の{0}$data{1}$delimiter{2}であり、すべての出力ソースには次のいずれかが必要です。

  1. {1}{2}の間に何もない
  2. |<>|{1}の間の{2}のみ
6
Kent Fredric
$line =~ s/[\r\n]+//g;
6
dsm

あなたの例では、あなたはただ行くことができます:

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
2
Curtis Yallop

上記の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'修飾子を使用するので、すべてを置き換えるまでダブルスペースを探し続けます。 (そのスペースを超えるものを効果的に置換)

1
freeworlder