Perlのラインブレイクを削除するための最もきちんとした方法

StackOverflow https://stackoverflow.com/questions/881779

  •  22-08-2019
  •  | 
  •  

質問

さまざまなソースから入力を取得できるスクリプトを維持し、1行ごとに機能します。使用される実際のソースに応じて、ラインブレイクはUnixスタイル、Windowsスタイル、または集計された入力に対してさえ、混合されている場合があります(!)。

ファイルから読むとき、それは次のようになります:

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

...

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

したがって、私がする必要があるのは、chompをUnixスタイルまたはWindowsスタイルのラインブレイクを削除するものに置き換えることです。私はこれを解決するためのあまりにも多くの方法を思いつきます、Perlの通常の欠点の1つ:)

一般的なラインブレイクを鳴らすための最もきちんとした方法についてのあなたの意見は何ですか?最も効率的なものは何ですか?

編集:小さな明確化 - メソッド「プロセス」はどこかから行のリストを取得します、 ファイルからnessecallyを読み取っていません. 。各ラインにはあります

  • 後続のラインブレイクはありません
  • Unixスタイルのラインブレイク
  • Windowsスタイルのラインブレイク
  • ただのキャリッジリターン(元のデータにWindowsスタイルのラインブレイクがあり、$/ = ' n'で読み取られた場合)
  • ラインが異なるスタイルを持つ集約セット
役に立ちましたか?

解決

少し掘り下げた後 Perlre 少しドキュメント、私はこれまでに私の最善の提案を提示しますそれはかなりうまく機能しているようです。 Perl 5.10は、一般化されたラインブレイクとして r文字クラスを追加しました。

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

それは次のものと同じです:

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

私はこの質問をしばらく開いたままにしておきます。

他のヒント

入力を経て、文字を削除または交換したいときはいつでも、このような小さなサブルーチンでそれを実行します。

sub clean {

    my $text = shift;

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

    return $text;
}

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

読む Perlport 私はようなものを提案します

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

どんなプラットフォームでも安全であるため、 rと nにあるものが異なるパールフレーバーによって異なる場合があるため、どのラインフィードスタイルでも処理している可能性があります。

$line =~ s/[\r\n]+//g;

2017年のメモ:設計の間違いと維持されていないエラーのため、ファイル:: Slurpは推奨されません。使用する ファイル:: Slurper また パス:: tiny 代わりは。

あなたの答えに拡張します

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

ファイル:: SlurpはファイルIOのものを抽象化し、文字列を返すだけです。

ノート

  1. の追加に注意することが重要です /g 、それなしでは、マルチラインの文字列が与えられた場合、それは 最初 違反のキャラクター。

  2. また、の除去 $, 、これはこの目的のために冗長です。 全て ラインブレーク、それ以前のラインブレークだけでなく、 $ このOSで。

  3. マルチライン文字列で、 $ の終わりと一致します ストリング そして、それは問題があるでしょう)。

  4. ポイント3は、ポイント2が使用したいという仮定で作成されることを意味します /m それ以外の場合は、「$」は、1行以上の文字列で実用的なものに対して基本的に意味がありません。 $ そして、それを見つけることができます \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」のバイトシーケンスがなかった場合、次に いいえ OS自身のものを除き、ラインブレイクは削除されます。

誰も私が話していることを誰も手に入れないように見えるので、ここに例があります、つまり テストいいえ ラインフィードを取り外します。それを実行すると、ラインフィードを入れておくことがわかります。

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

のために 明らかに 未処理の出力、こちらを参照してください: http://pastebin.com/f2c063d74

もちろん機能する特定の組み合わせがあることに注意してくださいが、それはおそらくあなた自身がテストされたものである可能性があります。

この出力では、すべての結果がフォームでなければならないことに注意してください >|$string|<>|$string|<ラインフィードはありません 有効な出力と見なされます。

$string 一般的な形です {0}$data{1}$delimiter{2} すべての出力ソースのどこにもあります。

  1. 間に何もありません {1}{2}
  2. それだけ |<>| の間に {1}{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

上記の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'修飾子を使用するため、それらすべてを置き換えるまで二重スペースを見つけ続けます。 (効果的に単一のスペースをその他に置き換える)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top