web-dev-qa-db-ja.com

Perlでは、ファイルの行を変更、削除、または挿入する方法、またはファイルの先頭に追加する方法を教えてください。

行の変更、削除、挿入、またはファイルの先頭への追加により、ファイルの内容を変更したい。 Perlでこれを行うにはどうすればよいですか?


これは 公式FAQ からの 質問 です。 perlfaqをStack Overflowにインポートする です。

27
perlfaq

(これは 公式のperlfaqの回答 であり、その後の編集を除く)

テキストファイルから行を挿入、変更、または削除する基本的な考え方には、変更を加えたいポイントまでファイルを読み取って印刷し、変更を加えてから、残りのファイルを読み取って印刷することが含まれます。 Tie :: File などのモジュールは偽造できますが、Perlは行へのランダムアクセスを提供しません(特に、レコード入力セパレーター$/は可変なので)。

これらのタスクを実行するPerlプログラムは、ファイルを開いてその行を印刷し、ファイルを閉じるという基本的な形式を取ります。

open my $in,  '<',  $file      or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";

while( <$in> )
    {
    print $out $_;
    }

close $out;

その基本フォーム内で、行の挿入、変更、または削除に必要なパーツを追加します。

行の先頭に追加するには、既存の行を印刷するループに入る前にそれらの行を印刷します。

open my $in,  '<',  $file      or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";

print $out "# Add this line to the top\n"; # <--- HERE'S THE MAGIC

while( <$in> )
    {
    print $out $_;
    }

close $out;

既存の行を変更するには、whileループ内の行を変更するコードを挿入します。この場合、コードは「Perl」のすべての小文字バージョンを検出し、それらを大文字にします。これはすべての行で発生するため、必ずすべての行で実行するようにしてください!

open my $in,  '<',  $file      or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";

print $out "# Add this line to the top\n";

while( <$in> )
    {
    s/\b(Perl)\b/Perl/g;
    print $out $_;
    }

close $out;

特定の行のみを変更するには、入力行番号$。が便利です。最初に、変更する行までを読んで印刷します。次に、変更する1行を読み、変更して、印刷します。その後、残りの行を読み、それらを印刷します。

while( <$in> )   # print the lines before the change
    {
    print $out $_;
    last if $. == 4; # line number before change
    }

my $line = <$in>;
$line =~ s/\b(Perl)\b/Perl/g;
print $out $line;

while( <$in> )   # print the rest of the lines
    {
    print $out $_;
    }

行をスキップするには、ループコントロールを使用します。この例の次の行はコメント行をスキップし、最後の行は__END__または__DATA__のいずれかを検出すると、すべての処理を停止します。

while( <$in> )
    {
    next if /^\s+#/;             # skip comment lines
    last if /^__(END|DATA)__$/;  # stop at end of code marker
    print $out $_;
    }

Nextを使用して出力に表示したくない行をスキップすることにより、特定の行を削除するために同じようなことを行います。この例では、5行ごとにスキップします。

while( <$in> )
    {
    next unless $. % 5;
    print $out $_;
    }

何らかの奇妙な理由で、行ごとに処理するのではなく、ファイル全体を一度に表示したい場合は、メモリに収まる限り、それを丸canみすることができます!

open my $in,  '<',  $file      or die "Can't read old file: $!"
open my $out, '>', "$file.new" or die "Can't write new file: $!";

my @lines = do { local $/; <$in> }; # Slurp!

    # do your magic here

print $out @lines;

File :: SlurpTie :: File などのモジュールも役立ちます。ただし、できる場合は、ファイル全体を一度に読み取らないでください。 Perlは、プロセスが終了するまでそのメモリをオペレーティングシステムに返しません。

Perlワンライナーを使用して、ファイルをインプレースで変更することもできます。以下は、inFile.txt内のすべての「Fred」を「Barney」に変更し、ファイルを新しい内容で上書きします。 -pスイッチを使用すると、Perlは-eで指定したコードをwhileループでラップし、-iでインプレース編集をオンにします。現在の行は$_にあります。 -pを使用すると、Perlは$_の値をループの最後に自動的に出力します。詳細については、 perlrun を参照してください。

Perl -pi -e 's/Fred/Barney/' inFile.txt

InFile.txtのバックアップを作成するには、-iに追加するファイル拡張子を付けます。

Perl -pi.bak -e 's/Fred/Barney/' inFile.txt

5行目のみを変更するには、テストチェック$.、入力行番号を追加し、テストが合格した場合にのみ操作を実行します。

Perl -pi -e 's/Fred/Barney/ if $. == 5' inFile.txt

特定の行の前に行を追加するには、Perlが$_を出力する前に行(または行!)を追加できます。

Perl -pi -e 'print "Put before third line\n" if $. == 3' inFile.txt

現在の行はループの最後に出力されるため、ファイルの先頭に行を追加することもできます。

Perl -pi -e 'print "Put before first line\n" if $. == 1' inFile.txt

すでにファイルにある行の後に行を挿入するには、-nスイッチを使用します。 -pに似ていますが、ループの最後に$_を出力しないので、自分で行う必要があります。この場合、最初に$_を印刷してから、追加する行を印刷します。

Perl -ni -e 'print; print "Put after fifth line\n" if $. == 5' inFile.txt

行を削除するには、必要な行のみを印刷します。

Perl -ni -e 'print unless /d/' inFile.txt
42
perlfaq