2

ファイルの上下に行を追加したい。私は次の方法でそれを行うことができます。

open (DATA, "</usr/old") || die "cant open old\n"; #file to which line has to be added

my @body=<DATA>;
close(DATA);

open (FILE, ">/usr/new") || die "cant open new\n"; #file after stuff has been added

print FILE "9   431";

print FILE "\n";

my $body=@body;

for (my $i=0; $i<$body;$i++){

    print FILE "$body[$i]";#not using for loop leads to addition of spaces in new file
}

print FILE "(3,((((1,4),(7,6)),(2,8)),5),9)";

大量のファイル セットを実行しているため、このプロセスには時間がかかります。Perl には、ファイルの先頭と末尾に行を追加するために使用される特定の機能はありますか?

4

9 に答える 9

13

perlfaq5の回答から、ファイルの行を変更、削除、挿入したり、ファイルの先頭に追加したりするにはどうすればよいですか?


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

(brian d foyによる寄稿)

テキストファイルから行を挿入、変更、または削除する基本的な考え方は、変更を加えたいポイントまでファイルを読み取って印刷し、変更を加えてから、ファイルの残りの部分を読み取って印刷することです。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 $_;
    }

奇妙な理由で、ファイルを1行ずつ処理するのではなく、ファイル全体を一度に表示したい場合は、ファイルを丸呑みすることができます(すべてをメモリに収めることができる限り)。

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::SlurpやTie::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のバックアップを作成するには、-iaファイル拡張子を付けて追加します。

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

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

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

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

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

    ... or ...

perl -pi -e 'next unless /d/' inFile.txt
于 2009-08-05T04:00:30.197 に答える
2

次の非常に悪い慣行を永続させる3つの答えが与えられました。

open(FILE,"<file") or die "cannot open";

それだけでなく、書き込み用ではなく読み取り用にファイルを開いているため、コードが壊れています。

オープンが失敗した場合、失敗した理由をユーザーに伝えることができます。$を含める習慣をつけてください!エラーメッセージで。また、の3つの引数形式を使用しopenて、モードを名前から分離します。

my $path="file";
open my($fh), '>', $path or die "$path: $!";

(これはあなたの質問に答えるものではありませんが、強調するためのコメントではなく答えにしているので、かなり長い吐き出しであるため、レビューすることができます。)

于 2009-08-05T03:33:58.000 に答える
2

Perl 配列を介してディスク ファイルの行にアクセスできるTie::Fileを使用します。標準配布で付いてきます。

ドキュメントの例:

use Tie::File;

tie @array, 'Tie::File', filename or die ...;
$array[13] = 'blah';     # line 13 of the file is now 'blah'
print $array[42];        # display line 42 of the file

$n_recs = @array;        # how many records are in the file?
$#array -= 2;            # chop two records off the end

for (@array) {
    s/PERL/Perl/g;         # Replace PERL with Perl everywhere in the file
}

# These are just like regular push, pop, unshift, shift, and splice
# Except that they modify the file in the way you would expect
push @array, new recs...;
my $r1 = pop @array;
unshift @array, new recs...;
my $r2 = shift @array;
@old_recs = splice @array, 3, 7, new recs...;

untie @array;            # all finished
于 2009-08-05T03:44:14.830 に答える
1

Perl は、ファイルの先頭に挿入することはできません。これを許可するオペレーティング システムはほとんどないためです。ここにあるタイプの書き換え操作が必要です。

そのコードで発生する可能性のある問題の 1 つは、アドレス空間に収まらない非常に大きなファイルの場合です。

ファイル全体を読み込んで書き出すと、メモリの問題が発生する可能性があります。私がしたことは、次のことです。

  • 現在のファイルの名前を変更する
  • 最初に挿入したいもので再作成し、
  • 名前を変更したファイルを大きなチャンク (必ずしも行ではない) で新しいファイルの末尾にコピーします。
  • 最後に新しいビットを追加します。

これにより、高速でメモリ効率が向上します。

もちろん、ファイルがメモリに収まるほど小さい場合は、持っているファイルに固執してください。それは十分です。

アップデート:

十分な数の人が、私がシェル スクリプトを正しかったと思って提唱しているという誤解を受けているようです。上記のすべてのことは、ネイティブ Perl 内から実行できます。

ただし、Perl を使用する必要があるかどうかを検討することをお勧めします。次のようなシェル コマンド:

( echo '9   431';cat /usr/old;echo '(3,((((1,4),(7,6)),(2,8)),5),9)' ) >/usr/new

トリックも同じように(そしておそらく同じくらい速く)実行します。

もちろん、 Perl が必要な場合は、この更新を老人のとりとめのないものとして無視してください :-)

于 2009-08-05T00:54:06.010 に答える
0

あなたはこれを行うことができます

open(FILE,">", $file) or die "cannot open $file: $!";
print FILE "add line to top\n";
while (<FILE>) { print $_ ."\n";}
close(FILE);
print FILE "add line to bottom\n";

コマンドラインで

perl myscript.pl > newfile
于 2009-08-05T00:57:23.183 に答える
0

There's many ways you can do it, for example with a simple shell script the way @Pax mentioned. You can also replace your array and loop with a join():

open(DATA, "</usr/old") || die "cant open old\n"; #file to which line has to be added
my $body=join("", <DATA>);
open (FILE, ">/usr/new") || die "cant open new\n"; #file after stuff has been added
print FILE "9   431\n";
print(FILE $body);
print FILE "(3,((((1,4),(7,6)),(2,8)),5),9)";
close(FILE);
于 2009-08-05T01:05:41.993 に答える
0

ghostdog74 に対する私の変更点は、ファイル ハンドルが print ステートメント内にある必要があり、2 番目の print ステートメントの後にファイルを閉じる必要があることです。

    open(FILE, ">", "file") or die "cannot open $file: $!"; 
    print FILE "add line to top";
    while (<FILE>) { print $_;}
    print FILE "add line to bottom";
    close(FILE);
于 2009-08-05T01:39:32.860 に答える
-1

Pax が言うように、これを行う組み込みの方法はありません。しかし、シェルから 1 行の perl コマンドで実行したい場合は、次を使用できます。

perl -ple 'print "Top line" if $. == 1; if (eof) { print "$_\nBottom line";  exit; }' yourfile.txt > newfile.txt
于 2009-08-05T01:25:38.440 に答える
-1

私は実際に Perl を話すわけではありませんが、おそらくこれはいくつかの状況でうまくいきます:

perl -0777 -pi -e 's/^/MY TEXT TO PREPEND/' myfile.txt

つまり、ファイルを段落モード (1 行) で開き、その行の先頭を新しいテキストに置き換えて、その場で書き換えます。

多くの大きなファイルにはおそらく効率的ではありません。

于 2015-12-21T12:30:36.470 に答える