ある二つのファイル(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);

このQ&Aに関連する最新のQ&A

A 回答 (3件)

原因については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で
対応する(ファイルの先頭から数えた行数が同じ)行を
照合することを意図しているようにも読めるんですが…。
    • good
    • 0
この回答へのお礼

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

どうやって時間を短縮さえ酔うと悩んでいたところです。
とても参考になります。

お礼日時:2001/07/11 10:05

 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から改行を抜いたデータが入れてある)
    • good
    • 0

二つ目のファイルを一回読み込んだら、読み込み位置がファイルの一番お尻に


あるからですね。

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

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

ってな感じ。

# perl は良く知らないんですけど、多分OK
    • good
    • 0
この回答へのお礼

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

お礼日時:2001/07/11 09:56

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Qperlスクリプト s/^\s+//;  s/\s+$//;  return wantarray ? @out : $out[0]; について

自作の掲示板を作ろうと思い、perlの勉強をしている者です。人様の作ったスクリプトを解析しています。以下のスクリプトはライブラリに記述されていたものです。


sub tttt {
my @out = @_;
for (@out) {
s/^\s+//;
s/\s+$//;
}
return wantarray ? @out : $out[0];


このスクリプトなんですが、 s/^\s+//; の部分の「+」と s/\s+$//; の部分の「+$」、 また「return wantarray~」 の三つの部分のスクリプトが、どういった働きをしているの分かりません。専門書やウェブ上のリファレンスも色々調べたのですが・・。

分かる方いらっしゃいましたらご教授下さると幸いです。よろしくお願いします。

Aベストアンサー

まず前2つの「+」は,正規表現における,「直前の表現を一回以上繰り返し」をあらわします.
「\s」は空白文字一文字を表す正規表現ですので,「\s+」は,「一文字以上の空白文字」になります.
次に,最初の「^」と2番目の「$」は,その正規表現がどこに現れるかを示す記号です.それぞれ,先頭と最後尾にあることを示します.
したがって,「^\s+」は,「最初に空白が一文字以上ある文字列」に,
「\s+$」は「行末に空白が一文字以上ある文字列」にヒットします.
置換構文sはご存知なんですかね.すなわち,この2文で,行の最初と最後の空白を消しているんです.

次に, wantarray ですが,
http://www2u.biglobe.ne.jp/~MAS/perl/ref/wantarray.html
によると,このサブルーチンttttを呼び出すときに,何を返り値にしているかで真偽が決まる関数です.
呼び出す時に配列を希望していたら @out 全体を,変数を希望していたら $out[0]だけを返します.

Q$wfurikae = 1 if ( &ccom::getShukujitsu( &com::tD( $wwy,$wwm,$wwd )) ne '' );がよく

$wfurikae = 1 if ( &ccom::getShukujitsu( &com::tD( $wwy,$wwm,$wwd )) ne '' );

というスクリプトがあったのですが、

$wfurikae = 1 の後に;もいれずifがきています。

違和感があります。

どのような意味になるのでしょうか。

宜しくお願い致します。

Aベストアンサー

そのまんまだと思いますよ。
if 以下の条件が真の時 $wfurikae = 1となります。
Perlはいろいろな書き方ができますので、こういう書き方もありです。
英語の文法の並びにする書き方です。

Qwhileの$_とforeachの$_の関係

以下のコードにおけるwhileとforeachでの$_の使いかたについて教えてください。

my @foos = qw( a b );

sub foo1 {
  open FH, "foo.txt";

  while (<FH>) {
    chomp;
    print "$_";
  }

#  while (my $x = <FH>) {
#    chomp $x;
#    print "$x";
#  }
  
  print "\n";
}

sub foo2 {
  foreach (@foos) {
    print "<$_>";
    foo1();
  }
  print "\n";
}

foo2();
foo2();

このコードと同じディレクトリに以下のような内容のfoo.txtをおいて実行します。

x
y

すると、以下のような結果が得られました。

<a>xy
<b>xy

<>xy
<>xy

一回目のfoo2で@foosの内容が消えてしまいます。一方、コメントアウトしてあるようにwhileに変数($x)を使うと期待したとおりの結果が出ます。なぜ、このような結果になるのでしょうか。ご存知の方がいらっしゃったら教えてください。宜しくお願いします。ちなみに、WinXP + ActivePerl(v5.8.6)で試しました。

以下のコードにおけるwhileとforeachでの$_の使いかたについて教えてください。

my @foos = qw( a b );

sub foo1 {
  open FH, "foo.txt";

  while (<FH>) {
    chomp;
    print "$_";
  }

#  while (my $x = <FH>) {
#    chomp $x;
#    print "$x";
#  }
  
  print "\n";
}

sub foo2 {
  foreach (@foos) {
    print "<$_>";
    foo1();
  }
  print "\n";
}

foo2();
foo2();

このコードと同じディレクトリに以下のような...続きを読む

Aベストアンサー

簡単に言うと
$_ をfoo1 とfoo2 で使っている為です。
こういうことがあるので
関数の中では使う変数はできるだけ局所的な使い方をするのがよろしいです。
最初の
foreach(@foos){
}
の中で$_ は、配列の中身のコピーではなくて、配列の要素そのものを指しているので、$_ への変更は直接@foosへの変更になってしまうので注意が必要です(この動作はこの動作で便利なものなんですが・)
なので、
foreach my $x (@foos) {
print "<$x>";
foo1();
}
のようにすれば良かったのかもしれません。
また、
sub foo1{
local $_;

}
のようにすれば、
foo1 で(foo2で使っていても)安心して$_ を使うことができます。

Q$hts =~ s/##([^#]+)##/$FORM{$1}/g の意味を教えてください!

お世話になります。
perl素人なのですが必要に迫られてWEBで調べながら
ソースを解析していますが、次のコードで完全に止まってしまいました。

$hts =~ s/##([^#]+)##/$FORM{$1}/g

この場合、
#hts から ##([^#]+)## を探して $FORM{$1} に全て置き換えようとしていると思うのですが、以下2点が理解できず困っています。

1.##([^#]+)## の意味
$htsに##で囲まれた文字列が複数あるのでそれら全てを探すということでしょうか?

2.$FORM{$1} の意味
$1は1.で検索した結果だと思いますが、$FORM{ }は一体なんでしょうか?

素人がいきなり解析するのは無謀なのは承知の上ですが、
どうしても業務で必要なので、お知恵をお貸し下さい。
よろしくお願いいたします。

Aベストアンサー

1.
perlを基準に「正規表現」で調べてごらん

2.
同じくperlでHTMLのformデータを受け取る方法を調べてごらん

Q[perl] $xxx == 1 or $xxx == 5 or $xxx == 11 などと書くが面倒です

タイトルの通りです

if文などで長々と書くのが面倒なのですが何かいい方法はないのでしょうか?
以前はif($xxx =~ /^1$|^5$|^11$/)などと書いていたのですが、正規表現を使うと処理が遅いんですね

なるべく処理が軽くて簡潔な書き方がありましたら紹介してください

Aベストアンサー

配列なら
my @array = (1, 5, 11);
if (grep {$_ == $xxx } @array) { ... }
とかかな. ハッシュなら
my %valid = ( 1 => 1, 5 => 1, 11 => 1 );
if ($valid{$xxx}) { ... }
のような感じ (「ハッシュを使った重複チェック」のバリエーション).
あ,
if ($xxx == (1 or 5 or 11)) { ... }
のような形は, (そのままじゃないけど) Perl6 でサポートされる予定になっています. いつのことかは知りませんが.


おすすめ情報