masatoの日記

やっていきます

Perlスクリプトでふたつのドキュメントから重複行を探し出す

翻訳を必要とするふたつのドキュメントがあったとして、外注に出すべきか自分でやるか判断したいとする。一見するとこのふたつの間には重複した箇所がたくさんあるのだが、どの程度の割合でそうなのかは厳密にはわからない。ほとんど同じ文なら外注に出すのはもったいない気がする。外注先によるが、文字数によって課金されるので、文章のなかに重複が含まれていても安くなったりはしないからだ。ユニーク/重複率がわかれば判断できるだが、人力では無理だという話である。Diffをとればよい、という意見もあるが、重複する行がドキュメント内に散らばっていると判別できない。こういう場面で文字列処理に強いPerlが活躍するのではないか。ということで、二つのファイルを比較して、ユニークな行を吐き出すスクリプトをつくってみた。

比較するソーステキストはテキストファイルになっているものとする。

use strict;
use warnings;

my $file1 = 'file1.txt';
my $file2 = 'file2.txt';

my %hash_file1 = ();
open my $fh1, '<', $file1 or die $!;
while(<$fh1>) {
    chomp;
    ++$hash_file1{$_};
}
close $fh1;

my %hash_file2 = ();
open my $fh2, '<', $file2 or die $!;
while(<$fh2>) {
    chomp;
    ++$hash_file2{$_};
}
close $fh2;

my @uniq = ();
for my $key ( keys %hash_file1 ) {
    if (! exists $hash_file2{$key} ) {
        push(@uniq, $key);
    }
}

open my $fh3, '>', 'dupe_lines.txt' or die $!;
for (@uniq) {
    print $fh3 $_,"\n";
}
close $fh3;

中身

愚直に順序を追って処理していくスクリプト

はじめに変数を二つ用意して、比較するふたつのファイル名をベタ打ちする。
標準入力から受け取ってもいいとおもう。

つぎに、ファイルを開いて、1行ずつハッシュのキーに入れる。ハッシュの値はなんでもいいので、++$hashってしてインクリメントさせている。なので、もし知りたかったらハッシュをダンプすれば、どの行がいくつ含まれているのかが、わかる。ハッシュのキーが行そのもの、値がそのカウントというわけ。

ふたつめのファイルで同じことを繰り返す。

そうしたら、ハッシュ1をループさせて、そのキー(=行そのもの)がハッシュ2に含まれているかを確認していく。exists関数を使えば、これがチェックできる。ここでは、含まれていない場合に@uniqにキーを入れて、重複していない行のみをしらべている。逆に重複している行をしらべたいときは、if (exists $hash_files{$key}と条件を逆にすればオーケーだ。うまくいけば、ユニークな(あるいは重複した)行のみをフィルターして配列に入れられる。

さいごに、フィルターした行が入っている配列をテキストファイルに書き出しておしまい。

感想

はじめ、なぜか上手くいかないと思ったら、ソースファイルの文字コードがそろっていなかった。
Wordからエディタにコピペしていると、これがよく起きる。で、うまくいかない理由がわからずに半ばパニックになるということを度々繰り返している。今回は20分くらいで気づけたので少しはましになってきた。