CSVファイルを読み込んで、入力データをそのまま出力するとともに、重複データに対して、最後に「重複」という文字をつけるスクリプトです。
基本的には、「3-2-1.キーの重複したデータを1つにする」と「3-2-2.キーが重複したデータだけを出力する」を組み合わせ、それぞれの出力ファイルをマージ処理する方法を採っています。
実際に応用する場合は、入出力データのファイル名などを変更してください。また、入力データはキー順に並べ替えてあることが前提となっています。Perlを利用して並べ替えを行う場合については、[2-4.ソート(並べ替え)処理]を参照してください。
なお、同様の処理を行う[3-2-6.ハッシュを使って、キーの重複したデータにマークをつけて、出力する]については、入力データをキー順に並べ替えておく必要はありません。
# double2.pl # 内容 : 重複チェック後、重複データには、「重複」をつける。 # 前提 : 重複チェックしたいキーであらかじめソート(並べ替え)しておく。 # Copyright (c) 2002-2011 Mitsuo Minagawa, All rights reserved. # (minagawa@fb3.so-net.ne.jp) # 使用方法 : c:\>perl double2.pl # # 初期値設定 $low_value = pack("h8","00000000"); #low-value $in1_key = $low_value; #入力キー $sv_key = $low_value; #保存した入力キー @in1 = (); #1件分の入力データ open(IN11,"double.txt"); open(OUT11,">single_temp.txt"); #重複データを除いたレコード while ($line1 = <IN11>) { chomp($line1); my $tmp = $line1; $tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); $in1_key = $in1[0]; if ($in1_key ne $sv_key) { $out1 = join(",",@in1); print OUT11 "$out1\n"; } $sv_key = $in1_key; } close(IN11); close(OUT11); #--------------------------------------------------- open(IN21,"double.txt"); open(OUT21,">double_temp.txt"); #重複のみのレコード # 初期値設定 $low_value = pack("h8","00000000"); #low-value $in1_key = $low_value; #入力キー $sv_key = $low_value; #1件前の保存キー $sv_key2 = $low_value; #2件前の保存キー @in1 = (); #1件分の入力データ $in1_ctr = 0; #入力件数 @save1 = (); #保存データ while ($line1 = <IN21>) { chomp($line1); $in1_ctr++; my $tmp = $line1; $tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); $in1_key = $in1[0]; if ($sv_key eq $in1_key) { $out1 = join(",",@save1,"重複"); print OUT21 "$out1\n"; $sv_key2 = $sv_key; } elsif (($sv_key2 eq $sv_key) && ($sv_key ne $low_value)) { $out1 = join(",",@save1,"重複"); print OUT21 "$out1\n"; } $sv_key = $in1_key; @save1 = @in1; } if (($in1_ctr > 0) && ($sv_key2 eq $sv_key)) { $out1 = join(",",@save1,"重複"); print OUT21 "$out1\n"; } close(IN21); close(OUT21); #------------------------------------------------ open(IN31,"double_temp.txt"); #重複のみのレコード open(IN32,"single_temp.txt"); #重複データを除いたレコード open(OUT31,">double_check.txt"); #マージしたファイル # 初期値設定 $high_value = pack("h8","ffffffff"); #終了判定 $low_value = pack("h8","00000000"); #low-value @in1 = (); #マスタデータ $in1_key = $low_value; #マスタキー @in2 = (); #トランザクションデータ $in2_key = $low_value; #トランザクションキー s_in1(); s_in2(); # # 入力データが終了するまで繰り返す。 # until (($in1_key eq $high_value) && ($in2_key eq $high_value)) { # # 主処理 # if ($in1_key eq $in2_key) { until ($in1_key ne $in2_key) { s_match(); s_in2(); } s_in1(); } 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 = <IN31>) { chomp($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 = <IN32>) { chomp($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(",",@in1); print OUT31 "$out1\n"; } # # マスターオンリーの処理 # sub s_master_only { $out2 = join(",",@in1); print OUT31 "$out2\n"; } # # トランザクションオンリーの処理 # sub s_trans_only { $out3 = join(",",@in2); print OUT31 "$out3\n"; } close(IN31); close(IN32); close(OUT31);
スクリプトはキーが重複しないように出力したレコードと、重複したレコードをマッチング(照合)することで、最初の入力データに対して、重複したレコードに「重複」の文字を表示するようにしたものです。
その他注意すべきこと
その他、注意すべき点は入力データがタブ区切りなどの場合とキーが複数ある場合の処理です。
1.入力データがタブ区切りの場合
下記のスクリプトはCSV2形式以外の引用符(")を使用する場合を含め、あらゆるCSVファイルに対応できるようになっていますが、入力データがタブ区切りの場合は
my $tmp = $line1; $tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
上記の部分を
@in1 = split("\t",$line1,-1);
と変更します。
2.キーが複数の項目から成り立っている場合
「$in1_key = $in1[0];」の部分を
$in1_key = $in1[0].$in1[1].$in1[2];
のように変更します(文字列の連結は「.」(ピリオド)を使う)。
aaa,1 aaa,2 aaa,3 bbb,1 ccc,1 ccc,2 ddd,1 ddd,2 ddd,3 ddd,4 eee,1 fff,1 ggg,1 hhh,1 hhh,2 hhh,3
aaa,1,重複 aaa,2,重複 aaa,3,重複 bbb,1 ccc,1,重複 ccc,2,重複 ddd,1,重複 ddd,2,重複 ddd,3,重複 ddd,4,重複 eee,1 fff,1 ggg,1 hhh,1,重複 hhh,2,重複 hhh,3,重複