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,重複