• 締切済み

2ファイルの比較

2ファイルの比較 中身が以下のようなファイルがあります。 ---ファイルA中身--- a c c d e f f ----------------- ---ファイルB中身--- a a b b b c d e g ----------------- この2ファイルから、以下のようなファイルを作りたいです。 ---ファイルC中身--- b b g ----------------- つまり、ファイルBにはあって、ファイルAにはない行をファイルCに入れたいです。 while($line_B = <FILEB>){ while($line_A = <FILEA>){ @A = split(/,/,$line_A); if($line_B !~ /$line_A/){ print FILEC $line_B; } } seek(FILEA,0,0); } としてみたのですが、全然ダメです。理由も分かってます。 が、お手上げです。ご教授お願いします。

みんなの回答

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.6

Perl 5.10 では「スマートマッチ」が追加されています. そのため, 今のケースでは grep すら使わず @A = <FILE_A>; # または @A = map { qr/^\Q\_\E$/ } <FILE_A>; for $line_B (<FILE_B>) { print FILE_C $line_B unless $line_B ~~ @A; } で終わりです. 完全に失念していたハッシュでも $A{$_} = 1 for (<FILE_A>); for $line_B (<FILE_B>) { print FILE_C $line_B unless $line_B ~~ %A; } と書けます... ん~, なんか手抜きのように見える....

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.5

確かに。 中身にメタ文字が含まれる可能性を失念していました。 @A=<FILEA> ; while($line_B = <FILEB>){ $reB=quotemeta($line_B) ; if( grep ( /^${reB}$/, @A ) == 0 ) { print FILEC $line_B; } } qrを使って「Aのどれかの行にマッチする」正規表現を作ってみました。 確かに、正規表現のコンパイルが1回で済み、マッチングも速そうです。 @A=(); while($line_A=<FILEA>){ push @A, '^' . quotemeta($line_A) ; } $lines_A=join('|',@A) $inA = qr/${linesA}/ ; undef $lines_A;undef @A ; #もう使わないのでundefしておく(メモリが稼げる?) while($line_B = <FILEB>){ if( $line_B !~ $inA ) { print FILEC $line_B; } } eqを使ったのも作ってみました @A=<FILEA> ; while($line_B = <FILEB>){ if( grep { $_ eq $line_B } @A ) == 0 ) { print FILEC $line_B; } } あるいは @A=<FILEA> ; while($line_B = <FILEB>){ $B_notin_A=1; foreach $line_A (@A) { if( $line_A eq $line_B ) { $B_notin_A=0; break; } } if ( $B_notin_A) { print FILEC $line_B; } } } grepとforeachのループとどちらが早いかは不明。最悪時間は組込みのgrepの方が早い気がするが、ヒット率が高いと途中で終れるforeahの方が早いかも。 ついでにもう一つ、私がよく使う方法はハッシュを使うものです。 %inA=(); while($line_A=<FILEA>){ $inA{$line_A}=1 ; } while($line_B = <FILEB>){ if( !defined( $inA{$line_B} ) ) { print FILEC $line_B; } } 改めて、Perlの自由度を思いしらされました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

ちと気になることがあるのですが>#3. ・eq で比較するのと正規表現を使うのとでは, どちらが速いんでしょうか. ・正規表現を使うなら, 「事前に読み込んだ」ファイルA の内容を正規表現に使う方がいいような気がします. qr で変換しておけばいいし. ・同じく正規表現を使うなら quotemeta しておかないとまずいのでは?

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.3

#FILEAを全部読めるくらいのメモリはあるとして。 @A=<FILEA> ; while($line_B = <FILEB>){ if( grep ( /^${line_B}$/, @A ) == 0 ) { print FILEC $line_B; } } > if($line_B !~ /$line_A/) AがBの部分になっていたときにもマッチします 例 $line_A="a\n" なら $line_B="aba\n" にマッチする。Aにないabaが「ある」と判定される

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

そりゃ, そこで出力したらそうなりますな. そうじゃなくて, 内側の while ループは「ファイルB から読み込んだ行 ($line_B) と同じ行がファイルA にあるかどうか」だけを調べて記憶し, そのループが*終わってから*「なければ出力」するのが普通. goto を使うかフラグを使うかはご自由に. もっとも, ファイルA の内容を全てメモリに入れることができるなら, いちいち読み込まずに最初に配列に入れておくべきだが.

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

なぜ「ダメ」なのか, 理由を書いてみてください. でも, @A の意味ってなんだろう.

cpcdchsh
質問者

補足

すみません。 while($line_B = <FILEB>){ while($line_A = <FILEA>){ if($line_B !~ /$line_A/){ print FILEC $line_B; } } seek(FILEA,0,0); } これでした。@Aの行は間違いです。 これだと、例えば、FILEBの1行目aとFILEAの全行の1行ずつを比べて マッチしなければFILECにaを書き込むということになっているので FILEAのaでない行の数だけFILECにaが書き込まれてしまうというのがダメな理由です。 説明下手ですみません。

関連するQ&A