OKWAVEのAI「あい」が美容・健康の悩みに最適な回答をご提案!
-PR-
解決
済み

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

  • すぐに回答を!
  • 質問No.101711
  • 閲覧数870
  • ありがとう数6
  • 気になる数0
  • 回答数3
  • コメント数0

お礼率 58% (7/12)

ある二つのファイル(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);
通報する
  • 回答数3
  • 気になる
    質問をブックマークします。
    マイページでまとめて確認できます。

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

  • 回答No.2
レベル9

ベストアンサー率 71% (59/82)

原因については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

お礼率 58% (7/12)

ありがとうございます。
確かに時間はさほど変わりませんでした。

どうやって時間を短縮さえ酔うと悩んでいたところです。
とても参考になります。
投稿日時 - 2001-07-11 10:05:16
-PR-
-PR-

その他の回答 (全2件)

  • 回答No.1
レベル14

ベストアンサー率 50% (1122/2211)

二つ目のファイルを一回読み込んだら、読み込み位置がファイルの一番お尻に あるからですね。 二つ目のファイルを処理しおわったら、巻き戻しましょう。 open(moto1_Log, ...); open(moto2_Log, ...); while ( <moto1_Log> ) {   ...   while ( <moto2_Log> ) {     .. ...続きを読む
二つ目のファイルを一回読み込んだら、読み込み位置がファイルの一番お尻に
あるからですね。

二つ目のファイルを処理しおわったら、巻き戻しましょう。

open(moto1_Log, ...);
open(moto2_Log, ...);
while ( <moto1_Log> ) {
  ...
  while ( <moto2_Log> ) {
    ...
  }
  seek(moto2_Log, 0, 0);  # ← これ
}

ってな感じ。

# perl は良く知らないんですけど、多分OK
お礼コメント
arowana

お礼率 58% (7/12)

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


  • 回答No.3
レベル12

ベストアンサー率 75% (398/526)

 2件の回答がついていますが、解決はされたのでしょうか?  質問に書かれたソースと、全く同じ動作をするコードを書いてみました。参考にしてみてください。   my %moto2;   open IN, 'moto2.csv';   while (<IN>) {     chomp;     my $_2nd = (split(/,/))[1];      ...続きを読む
 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から改行を抜いたデータが入れてある)
このQ&Aで解決しましたか?
関連するQ&A
-PR-
-PR-
こんな書き方もあるよ!この情報は知ってる?あなたの知識を教えて!
このQ&Aにはまだコメントがありません。
あなたの思ったこと、知っていることをここにコメントしてみましょう。

その他の関連するQ&A、テーマをキーワードで探す

キーワードでQ&A、テーマを検索する
-PR-
-PR-
-PR-

特集


いま みんなが気になるQ&A

関連するQ&A

-PR-

ピックアップ

-PR-
ページ先頭へ