34

Perlにファイルハンドルがあり、ファイルFILEのすべての行を繰り返し処理したいと思います。次の違いはありますか?

while (<FILE>) {
    # do something
}

foreach (<FILE>) {
    # do something
}
4

8 に答える 8

40

ほとんどの場合、違いに気付かないでしょう。ただし、foreach各行を(配列ではなくリストに読み込んでから、1行ずつ読み取りますが、一度に1行ずつ読み取ります。より多くのメモリを使用し、事前に処理時間を必要とするため、通常、ファイルの行を反復処理するために使用することをお勧めします。whileforeachwhile

編集(Schwern経由):foreachループはこれと同等です:

my @lines = <$fh>;
for my $line (@lines) {
    ...
}

残念ながら、Perlは範囲演算子()の場合のようにこの特殊なケースを最適化しません1..10

たとえば、forループとループを使用して/ usr / share / dict / wordsを読み取り、それらが完了したらスリープ状態にすると、プロセスが消費しているメモリの量を確認whileできます。psコントロールとして、ファイルを開くが何もしないプログラムを含めました。

USER       PID %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
schwern  73019   0.0  1.6   625552  33688 s000  S     2:47PM   0:00.24 perl -wle open my $fh, shift; for(<$fh>) { 1 } print "Done";  sleep 999 /usr/share/dict/words
schwern  73018   0.0  0.1   601096   1236 s000  S     2:46PM   0:00.09 perl -wle open my $fh, shift; while(<$fh>) { 1 } print "Done";  sleep 999 /usr/share/dict/words
schwern  73081   0.0  0.1   601096   1168 s000  S     2:55PM   0:00.00 perl -wle open my $fh, shift; print "Done";  sleep 999 /usr/share/dict/words

このプログラムは、2.4メガの/ usr / share / dict / wordsの内容を格納するためにfor、約32メガの実メモリ(列)を消費しています。RSSループはwhile一度に1行しか格納せず、行のバッファリングに70kしか消費しません。

于 2009-02-25T09:50:32.013 に答える
19

スカラーコンテキスト(つまりwhile)では<FILE>、各行を順番に返します。

リストコンテキスト(つまりforeach)では<FILE>、ファイルの各行で構成されるリストを返します。

whileコンストラクトを使用する必要があります。

詳細については、perlop-I/O演算子を参照してください。

編集:j_random_hackerは正しくそれを言います

while (<FILE>) { … }

foreachが踏みにじらない間、踏みつけます$_(foreachは$_最初にローカライズします)。確かに、これは最も重要な動作の違いです!

于 2009-02-25T09:46:38.077 に答える
11

前の回答に加えて、使用する別の利点は、変数whileを使用できることです。$.これは、最後にアクセスされたファイルハンドルの現在の行番号です ( を参照perldoc perlvar)。

while ( my $line = <FILE> ) {
    if ( $line =~ /some_target/ ) {
        print "Found some_target at line $.\n";
    }
}
于 2009-02-25T15:43:37.033 に答える
4

これを扱った例を、Effective Perl Programmingの次の版に追加しました。

を使用するwhileと、処理を停止FILEしても未処理の行を取得できます。

 while( <FILE> ) {  # scalar context
      last if ...;
      }
 my $line = <FILE>; # still lines left

を使用すると、処理を停止してもforeach、すべての行が消費されます。foreach

 foreach( <FILE> ) { # list context
      last if ...;
      }
 my $line = <FILE>; # no lines left!
于 2010-02-04T02:38:20.057 に答える
3

更新: j random hacker はコメントで、ファイル ハンドルから読み取るときに、Perl が while ループで falseness テストを特殊なケースにしていることを指摘しています。偽の値を読み取ってもループが終了しないことを確認しました -- 少なくとも最近の perl では。間違った方向に進んで申し訳ありません。Perl を書き始めて 15 年になりますが、私はまだ初心者です。;)

上記の誰もが正しいです。whileループを使用すると、メモリ効率が向上し、より多くの制御が可能になります。

ただし、このループの面白い点whileは、読み取りが false の場合にループが終了することです。通常、これはファイルの終わりになりますが、空の文字列または 0 が返された場合はどうなるでしょうか? おっとっと!あなたのプログラムはあまりにも早く終了しました。これは、ファイルの最後の行に改行がない場合、どのファイル ハンドルでも発生する可能性があります。また、通常の Perl ファイル オブジェクトと同じように改行を処理しない read メソッドを持つカスタム ファイル オブジェクトでも発生する可能性があります。

これを修正する方法は次のとおりです。ファイルの終わりを示す未定義の値の読み取りを確認します。

while (defined(my $line = <FILE>)) {
    print $line;
}

ちなみに、ループにはこのforeach問題はなく、非効率的ですが正しいです。

于 2009-02-26T01:06:08.803 に答える
2

j_random_hackerは、この回答へのコメントでこれについて言及しましたが、言及する価値のある別の違いであっても、実際には独自の回答には入れませんでした。

違いは、ローカライズしながらwhile (<FILE>) {}上書きすることです。あれは:$_foreach(<FILE>) {}

$_ = 100;
while (<FILE>) {
    # $_ gets each line in turn
    # do something with the file
}
print $_; # yes I know that $_ is unneeded here, but 
          # I'm trying to write clear code for the example

の最後の行を出力し<FILE>ます。

でも、

$_ = 100;
foreach(<FILE>) {
    # $_ gets each line in turn
    # do something with the file
}
print $_;

印刷し100ます。コンストラクトで同じことを行うには、次のwhile(<FILE>) {}ことを行う必要があります。

$_ = 100;
{
    local $_;
    while (<FILE>) {
        # $_ gets each line in turn
        # do something with the file
    }
}
print $_; # yes I know that $_ is unneeded here, but 
          # I'm trying to write clear code for the example

これで印刷されます100

于 2009-08-29T11:30:55.793 に答える
1

foreachこれは機能しないが仕事をwhileする例です

while (<FILE>) {
   $line1 = $_;
   if ($line1 =~ /SOMETHING/) {
      $line2 = <FILE>;
      if (line2 =~ /SOMETHING ELSE/) {
         print "I found SOMETHING and SOMETHING ELSE in consecutive lines\n";
         exit();
      }
   }
}

foreachループに入る前にファイル全体をリストに読み込み、ループ内の次の行を読み取ることができないため、これを行うことはできません。foreach でもこの問題の回避策があると確信していますが (配列への読み込みが思い浮かびます)、非常に簡単な解決策を確実に提供します。

2 番目の例は、2 GB の RAM しかないマシンで大きな (たとえば 3 GB の) ファイルを解析する必要がある場合です。foreach単にメモリ不足になり、クラッシュします。私はこれを、perl プログラミングの人生の非常に早い段階で難しい方法で学びました。

于 2010-03-11T14:08:28.507 に答える
0

foreach ループは while (条件付き) よりも高速です。

于 2010-08-20T01:52:33.963 に答える