2つのCSVファイルを読み込んで、両方のファイルを1つにまとめるマージ処理を行うスクリプトです。
「1対1マッチング(照合)」を変形した処理であり、「マスタファイル」と「トランザクションファイル」の両方から統合した1つのファイルを作成する処理です。
注意すべき点は、出力ファイルにあわせてファイルハンドルを設定する点です。この例では、出力ファイルは1つのため、対応するファイルハンドルも1つになっています。仮に「マッチング(照合)したファイル」と「トランザクションファイル」を1つのファイルに出力し、「マスタオンリーファイル」と区別する場合は、ファイルハンドルを2つ設定することになります。マッチング(照合)用のスクリプトをそのまま持ってきて、ファイル名だけを変更すると誤った出力結果になるので注意する必要があります。
下記に示したのは、スクリプトの構造を示したPAD図です。この図と実際のスクリプトを比較しながら、内容を理解してください。なお、入力データはキー順に並べ替えてあることが前提となっています。Perlを利用して並べ替えを行う場合については、[2-4.ソート(並べ替え)処理]を参照してください。
並べ替えをしなくてもマージ処理を行うことができるスクリプトについては[2-3-6.ハッシュを使ったマージ処理]にあります。
統合された1つのファイルに対して、「マスタファイル」や「トランザクションファイル」からどのようにデータをセットするかは、それぞれのケースによって異なるので、各自工夫してください。
「1対1マッチング(照合)」の変形だからといって、複数の異なる出力ファイルのファイルハンドルに対して、同一のデータを指定しないでください。処理の結果が保証されなくなりますので、十分注意してください。
# merge.pl # 内容 : マージプログラム # (2ファイルの1対1マッチングをした後、1ファイルに統合する) # 前提 : マスタファイルとトランザクションファイルの両方を # マージするキーごとに昇順に並べ替えておくこと # Copyright (c) 2002-2011 Mitsuo Minagawa, All rights reserved. # (minagawa@fb3.so-net.ne.jp) # 使用方法 : c:\>perl merge.pl # # ファイルのオープン open(IN1,"master.txt"); #マスタファイル open(IN2,"transaction.txt"); #トランザクションファイル open(OUT1,">merge.txt"); #マージしたファイル # 初期値設定 $high_value = pack("h8","ffffffff"); #終了判定 $low_value = pack("h8","00000000"); #low-value $in1_key = $low_value; #マスタキー $in2_key = $low_value; #トランザクションキー # 1件目のデータ入力 s_in1(); s_in2(); # 主処理 until ($in1_key eq $high_value && $in2_key eq $high_value) { # マッチング(照合)の時(両方のファイルにデータがある) if ($in1_key eq $in2_key) { s_match(); s_in1(); s_in2(); } # マスタオンリーの時(マスタファイルだけにデータがある) elsif ($in1_key lt $in2_key) { s_master_only(); s_in1(); } # トランザクションオンリーの時(トランザクションファイルだけにデータがある) elsif ($in1_key gt $in2_key) { s_trans_only(); s_in2(); } } # マスタファイル入力 sub s_in1 { if ($line1 = <IN1>) { chomp($line1); #---区切り文字により、処理を変更する---------- #タブ区切りのとき # @in1 = split("\t",$line1); #カンマ区切りのとき my $tmp = $line1; $tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); #----------------- $in1_key = $in1[0]; } else { #マスタファイルが終了のとき $in1_key = $high_value; } } # トランザクションファイル入力 sub s_in2 { if ($line2 = <IN2>) { chomp($line2); #---区切り文字により、処理を変更する---------- #タブ区切りのとき # @in2 = split("\t",$line2); #カンマ区切りのとき my $tmp = $line2; $tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in2 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); #----------------- $in2_key = $in2[0]; } else { #トランザクションファイルが終了のとき $in2_key = $high_value; } } # マッチング(照合)時の処理 sub s_match { $out1 = join("\t",@in1); print OUT1 "$out1\n"; } # マスタオンリー時の処理 sub s_master_only { $out2 = join("\t",@in1); print OUT1 "$out2\n"; } # トランザクションオンリー時の処理 sub s_trans_only { $out3 = join("\t",@in2); print OUT1 "$out3\n"; } # ファイルのクローズ close(IN1); close(IN2); close(OUT1);
マージ処理
# マッチング(照合)時の処理 sub s_match { $out1 = join("\t",@in1); print OUT1 "$out1\n"; } # マスタオンリー時の処理 sub s_master_only { $out2 = join("\t",@in1); print OUT1 "$out2\n"; } # トランザクションオンリー時の処理 sub s_trans_only { $out3 = join("\t",@in2); print OUT1 "$out3\n"; }
マージ処理はマッチング(照合)処理とほぼ同じような処理になりますが、出力先が1つになっている点が異なります。マッチング(照合)処理のように出力用のファイルハンドルを3つ用意して、それぞれに同じ出力ファイルを割り当てるようなことはしないでください。
その他注意すべきこと
その他、注意すべき点は入力データがタブ区切りなどの場合とキーが複数ある場合の処理です。
上記のスクリプトでは、入力ファイルがCSVファイルであれば、どのようなものでも対応できるようになっています。具体的には以下の箇所になります。
CSV形式の入力データを配列に変換する
my $tmp = $line1; $tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
上記のスクリプトはCSV2形式以外の引用符(")を使用する場合を含め、あらゆるCSVファイルに対応できるようになっています(CSV2形式などについては、[3-1-1.固定長データとCSVデータ]を参照してください)が、このCSVファイルに分解するロジックは、大崎 博基(OHZAKI Hiroki)さんの「Perlメモ」に記載されていたものを参考にしています。また、CSV2形式のCSVファイルであれば、上記のスクリプトは以下のように簡略化できます。
1.入力データがCSV2形式の場合、
@in1 = split(",",$line1,-1);
とします。
2.入力データがタブ区切りの場合、
@in1 = split("\t",$line1,-1);
とします。
3.キーが複数の項目から成り立っている場合は、
「$in1_key = $in1[0]」の部分を
$in1_key = $in1[0].$in1[1].$in1[2];
のように変更します(文字列の連結は「.」(ピリオド)を使います)。
01,11111 02,22222 03,33333 05,44444 07,77777 08,88888 09,99999
01,100 02,200 04,400 06,600 07,700 08,800 09,900 10,1000
01,11111 02,22222 03,33333 04,400 05,44444 06,600 07,77777 08,88888 09,99999 10,1000