CSVファイルを読み込んで、入力データをそのまま出力するとともに、重複データに対して、最後に「重複」という文字をつけるスクリプトです。
キーごとにハッシュにデータをセットしておき、2件以上ある場合に「重複」という文字を付けて、出力しています。
実際に応用する場合は、入出力データのファイル名などを変更してください。
なお、[3-2-3.キーの重複したデータにマークをつけて、出力する]と異なり、入力データはキー順に並べ替えておく必要はありません。
# double2_hash.pl # 内容 : 重複チェック後、重複データには、「重複」表示をする。 # 前提 : 重複チェックしたいキーであらかじめソートしておく。 # Copyright (c) 2013 Mitsuo Minagawa, All rights reserved. # (minagawa@fb3.so-net.ne.jp) # 使用方法 : c:\>perl double2_hash.pl # # 出力用ハッシュ my %out1; # マスタデータをハッシュに入れる open(IN1,"double.txt"); while($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]; # ハッシュのキーがすでに存在する場合 if (exists $out1{$in1_key}) { @double = @{$out1{$in1_key}}; $double[0] += 1; #配列の0番目に件数を加算 push(@double,$line1); #配列の2番目以降にデータ追加 $out1{$in1_key} = [@double]; } # ハッシュのキーが存在しない場合 else { @double = (); $double[0] = 1; #配列の0番目に件数をセット push(@double,$line1); #配列の1番目にデータをセット $out1{$in1_key} = [@double]; } } close(IN1); open(OUT1,">double_check.txt"); # ハッシュのキーをソートして出力する foreach my $key (sort keys %out1) { # 件数が1件のとき(重複データでないとき) if (${$out1{$key}}[0] == 1) { shift(@{$out1{$key}}); #件数の入った配列の0番目を削除 print OUT1 join("\n", @{$out1{$key}})."\n"; } # 件数が2件以上のとき(重複データのとき) else { shift(@{$out1{$key}}); #件数の入った配列の0番目を削除 foreach $data (@{$out1{$key}}) { $temp = join(",",$data,"重複"); print OUT1 $temp."\n"; } } } close(OUT1);
それでは、スクリプトの解説を個別に行っていきましょう。
重複しているかどうかで振り分ける
# ハッシュのキーがすでに存在する場合 if (exists $out1{$in1_key}) { @double = @{$out1{$in1_key}}; $double[0] += 1; #配列の0番目に件数を加算 push(@double,$line1); #配列の2番目以降にデータ追加 $out1{$in1_key} = [@double]; } # ハッシュのキーが存在しない場合 else { @double = (); $double[0] = 1; #配列の0番目に件数をセット push(@double,$line1); #配列の1番目にデータをセット $out1{$in1_key} = [@double]; }
ハッシュのデータ部分を配列にしています。そして0番目の要素に同じキーが存在する件数を入れ、1番目以降の要素に実際のデータを入れていきます。"@{$out1{$in1_key}}"とすることで、ハッシュの要素を配列として、別の配列に代入することができます。また、"[@double]"とすることで、配列をハッシュのデータにセットすることができます。
ハッシュのキーをソートして出力する
# ハッシュのキーをソートして出力する foreach my $key (sort keys %out1) { # 件数が1件のとき(重複データでないとき) if (${$out1{$key}}[0] == 1) { shift(@{$out1{$key}}); #件数の入った配列の0番目を削除 print OUT1 join("\n", @{$out1{$key}})."\n"; } # 件数が2件以上のとき(重複データのとき) else { shift(@{$out1{$key}}); #件数の入った配列の0番目を削除 foreach $data (@{$out1{$key}}) { $temp = join(",",$data,"重複"); print OUT1 $temp."\n"; } } }
"${$out1{$key}}[0]"とすることで、ハッシュのデータ部分の配列の0番目の要素を取り出すことができます。0番目の要素にキーごと件数をセットしていますので、1件だけの場合、そのまま出力し、2件以上ある場合、配列の要素を個別に出力しています。その際、0番目の要素は"shift"で削除してから出力します。
その他注意すべきこと
その他、注意すべき点は入力データがタブ区切りなどの場合とキーが複数ある場合の処理です。
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);
と変更します。また、下記の箇所を
foreach $data (@{$out1{$key}}) { $temp = join(",",$data,"重複"); print OUT1 $temp."\n"; }
以下のように変更します。
foreach $data (@{$out1{$key}}) { $temp = join("\t",$data,"重複"); print OUT1 $temp."\n"; }
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,重複