• ベストアンサー

Perlの初心者です。2重ループの方法で困ってます。

ある二つのファイル(moto1.csvとmoto2.csv)の2番目のフィールドが おなじときに二つのファイルの中身をあわせて別のファイル(kekka.csv)を 作る作業をしています。 下記のソースで※2の場所で何回もファイルをオープンさせるととても重いので ※1でファイルを一回だけオープンさせて処理しようと思ったら。 内側のループ(moto2_Log)が一回しか処理されないので困っております。 何かよいアイデアがありましたらよろしくお願いします。 open(moto1_Log,"< moto1.csv"); open(kekka_Log,"> kekka.csv"); ※1open(moto2_Log,"< moto2.csv"); while( <moto1_Log> ) { chop; @moto1_List=split(/,/); ※2 #open(moto2_Log,"< moto2.csv"); while( <moto2_Log> ) { chop; @moto2_List=split(/,/); if($moto1_[1] eq $moto2_List[1]){ print kekka_Log $S_List[0]; print kekka_Log ","; print kekka_Log $S_List[1]; print Export_Log ","; print Export_Log $S_List[2]; print Export_Log ","; print Export_Log $S_List[3]; print Export_Log ":"; print Export_Log $E_List[0]; print Export_Log ","; print Export_Log $E_List[1]; print Export_Log ","; print Export_Log $E_List[2]; print Export_Log ","; print Export_Log $E_List[3]; print Export_Log "\n"; #改行コード continu; } } } close (moto2_Log); close(kekka_Log); close(moto1_Log);

質問者が選んだベストアンサー

  • ベストアンサー
  • Fooky
  • ベストアンサー率71% (59/82)
回答No.2

原因についてはa-kumaさんが仰ってる通りですので、 代替案を挙げたいと思います。 「何回もファイルをオープンさせるととても重いので」という ことですが、ファイルを一々巻き戻す(seekする)現在の方法でも、 実行時間としてはほとんど変わらないと思いますよ。 openにかかる時間が中身をリードする時間と比較して、 誤差以上に意味のある時間になるとは思えません。 同様のプログラムをつくって、moto1.csvとmoto2.csvとして 500行のテキストファイル(具体的には500行に切り詰めた Linuxのシステムログ/var/log/messagesファイル) を使って実験したところ、 一々オープンする方法の実行時間(47.63秒)は、 巻き戻す方法の実行時間(47.28秒)と比較して、 たった0.7%しか増加しませんでした。 この実験では、moto2.csvは全てキャッシュに乗っていますが、 キャッシュに乗りきらないほど大きなファイルになったとしても、 さして結果は変わらないと思います。 そこで代替案ですが、一旦、片方のファイルを全て 配列に読み込んではどうでしょう? open(moto2_Log, "<moto2.csv"); my @moto2_List; while(<moto2_Log>){  chomp;  my @cols = split(/,/);  push(@moto2_List, \@cols); } close(moto2_Log); open(moto1_Log, "<moto1.csv"); open(kekka_Log, ">kekka.csv"); while(<moto1_Log>){  chomp;  my @moto1_List = split(/,/);  foreach $m2lst ( @moto2_List ){   if( $moto1_List[1] eq $m2lst->[1] ){    print kekka_Log ...   }  } } close(kekka_Log); close(moto1_Log); 上の実験と同じ500行のテキストファイルに対して、 この方法だと実行時間は約5分の1の、10.17秒と なりました。まあ、ファイルアクセスの時間だけ じゃなくて、splitの回数も減ってるので、その 影響もあるんでしょう。 ところで、このアルゴリズムだと、moto1.csvの 中の各行とmoto2.csvの中の全行を照合してますが、 それはそれで合ってるんでしょうか? > 2番目のフィールドが > おなじときに二つのファイルの中身をあわせて別のファイル(kekka.csv)を > 作る という辺りから、moto1.csvの各行と、moto2.csvで 対応する(ファイルの先頭から数えた行数が同じ)行を 照合することを意図しているようにも読めるんですが…。

arowana
質問者

お礼

ありがとうございます。 確かに時間はさほど変わりませんでした。 どうやって時間を短縮さえ酔うと悩んでいたところです。 とても参考になります。

その他の回答 (2)

  • leaz024
  • ベストアンサー率75% (398/526)
回答No.3

 2件の回答がついていますが、解決はされたのでしょうか?  質問に書かれたソースと、全く同じ動作をするコードを書いてみました。参考にしてみてください。   my %moto2;   open IN, 'moto2.csv';   while (<IN>) {     chomp;     my $_2nd = (split(/,/))[1];     $moto2{$_2nd} = $_ unless defined $moto2{$_2nd};   }   close IN;   open OUT, '>kekka.csv';   open IN, 'moto1.csv';   while (<IN>) {     chomp;     my $_2nd = (split(/,/))[1];     print OUT "$_, $moto2{$_2nd}\n" if defined $moto2{$_2nd};   }   close IN;   close OUT; ○moto2.csvの2番目のデータをキーにしたハッシュを作成します。  値はそのデータの行全体(改行は抜いたもの)にします。後で結局カンマ区切りの合成をするので、そのまま使うわけです。 ○moto1.csvを開いて1行ずつ読み、2番目のデータを取り出します。  このデータを%moto2のKEYにして値があれば、moto1とmoto2で全く同じデータが存在するわけです。 ○kekka.csvには、2番目に同じデータがある行の全項目をカンマ区切りにしたデータを入れるので、split前のデータから改行を抜いたもの同士をカンマを挟んで書き込みます。  (%moto2のVALUEには、moto2.csvから改行を抜いたデータが入れてある)

  • a-kuma
  • ベストアンサー率50% (1122/2211)
回答No.1

二つ目のファイルを一回読み込んだら、読み込み位置がファイルの一番お尻に あるからですね。 二つ目のファイルを処理しおわったら、巻き戻しましょう。 open(moto1_Log, ...); open(moto2_Log, ...); while ( <moto1_Log> ) {   ...   while ( <moto2_Log> ) {     ...   }   seek(moto2_Log, 0, 0);  # ← これ } ってな感じ。 # perl は良く知らないんですけど、多分OK

arowana
質問者

お礼

seekですね 理由は何と無く分かったいました。 戻し方が解りませんでした(リファレンスだけだと探すのが大変です) ありがとうございます。

関連するQ&A