CSVファイルを文字列型や数値型に区別して、昇順にも降順にもソート(sort:並べ替え)することのできるスクリプトです。
数値型というのは、数字を計算するための数値として扱うということで、たとえば、昇順に並べ替えると「1,2,3,10,20,30」のような順番になりますが、文字列型では「1,10,2,20,3,30」のような順番になります。文字列型・数値型というのは、データそのものによって決まるのではなく、データをどのように扱う(認識する)かによって決まりますので、データが数字だからといって数値型と即断するのは誤りです(文字列型の可能性もあります)。Excelなどの表計算ソフトなどでは、この点を誤解しているようです。
並べ替える項目が何番目の項目かを指定し、その項目をどのように並べ替えるかを指定することによって、文字列型や数値型で昇順または降順で並べ替えをしていきます。並べ替えの方法は挿入ソートを元にしています。なお、実用的な入力データの件数は指定するキーの数にもよりますが、ソート(sort:並べ替え)キーを2つ指定したときで、数千件程度までです。それ以上の入力件数がある場合は、実用にはなりません(ソート(sort:並べ替え)キーを増やせば、さらに入力件数の限度は少なくなります)。
実際に応用する場合は、入出力データのファイル名を変更し、「@sort_item = (1,0)」の部分を変更します。この例では、2番目の項目を第1キーに、1番目の項目を第2キーとして並べ替えるように指定しています。Perlでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。
また、並べ替えの方法は「@sort_type = ("NA","CA")」の部分を変更します。この例では、2番目の項目を数値型の昇順に、1番目の項目を文字列型の昇順で並べ替えるように指定しています。各項目は「@sort_item = (1,0)」で指定したものと対応させます。数字のない項目や「カンマつきの数字」、「通貨記号付きの数字」は数値型とはみなされませんので、注意してください。
下記の例では、フィールドを「"」(ダブルクォーテーションつき)で囲み、それぞれのデータを「,」(カンマ)で区切っている形式を前提としているため、
$line1 =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($line1 =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
としていますが、フィールドを「"」(ダブルクォーテーションつき)で囲まずに、それぞれのデータを「,」(カンマ)で区切っている、いわゆる「CSV2形式」であれば、以下のようにすることができます。
@in1 = split(",",$line1,-1);
また、タブ区切りであれば、以下のようにすることができます。
@in1 = split("\t",$line1,-1);
# csvsort3.pl # 内容 : 対象ファイルをCSV形式としてソート(並べ替え)する # 文字列型の昇順・降順、数値型の昇順・降順の並べ替えを行う。 # (通貨記号や","つきの数字は数値型としての並べ替えはできない) # Copyright (c) 2009 Mitsuo Minagawa, All rights reserved. # (minagawa@fb3.so-net.ne.jp) # 使用方法 : c:\>perl csvsort3.pl # open(IN1,"input.txt"); open(OUT1,">output_s.txt"); # 整列の基準となる項目番号 # ソート(並べ替え)キー(2番目と1番目の項目) @sort_item = (1,0); # ソート(並べ替え)キーの入っている項目のデータ構造と昇順か降順かの指定 # 文字列型:C 数値型:N/昇順:A 降順:D と指定する。 # 例:文字列型の降順:CD、数値型の昇順:NA、数値型の降順:ND @sort_type = ("NA","CA"); $in1_ctr = 0; # CSVファイルの入力 while ($line1 = <IN1>) { chomp($line1); $line1 =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($line1 =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); $in1_rec[$in1_ctr] = join("\t",@in1); $in1_ctr++; } # ソート(並べ替え)項目の分だけ繰り返し # 並べ替え処理(挿入ソート) @w_sort_type = reverse(@sort_type); @w_sort_item = reverse(@sort_item); for ($k = 0; $k <= $#w_sort_type; $k++) { $n = $w_sort_item[$k]; if ($w_sort_type[$k] eq "CA") { #文字列型の昇順 for ($i = 0; $i <= $#in1_rec; $i++) { for ($j = 1; $j <= ($#in1_rec - $i); $j++) { $in1_key_a = (split("\t",$in1_rec[$j - 1]))[$n]; $in1_key_b = (split("\t",$in1_rec[$j]))[$n]; if ($in1_key_a gt $in1_key_b) { #逆順の時 ($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]); } } } } elsif ($w_sort_type[$k] eq "CD") { #文字列型の降順 for ($i = 0; $i <= $#in1_rec; $i++) { for ($j = 1; $j <= ($#in1_rec - $i); $j++) { $in1_key_a = (split("\t",$in1_rec[$j - 1]))[$n]; $in1_key_b = (split("\t",$in1_rec[$j]))[$n]; if ($in1_key_a lt $in1_key_b) { #逆順の時 ($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]); } } } } elsif ($w_sort_type[$k] eq "NA") { #数値型の昇順 for ($i = 0; $i <= $#in1_rec; $i++) { for ($j = 1; $j <= ($#in1_rec - $i); $j++) { $in1_key_a = (split("\t",$in1_rec[$j - 1]))[$n]; $in1_key_b = (split("\t",$in1_rec[$j]))[$n]; if ($in1_key_a > $in1_key_b) { #逆順の時 ($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]); } } } } elsif ($w_sort_type[$k] eq "ND") { #数値型の降順 for ($i = 0; $i <= $#in1_rec; $i++) { for ($j = 1; $j <= ($#in1_rec - $i); $j++) { $in1_key_a = (split("\t",$in1_rec[$j - 1]))[$n]; $in1_key_b = (split("\t",$in1_rec[$j]))[$n]; if ($in1_key_a < $in1_key_b) { #逆順の時 ($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]); } } } } } # ソート(並べ替え)済ファイルの出力 foreach $record (@in1_rec) { print OUT1 "$record\n"; } close(IN1); close(OUT1);
それでは、1つずつ解説していきましょう。
ソート(並べ替え)キーのセット
@w_sort_type = reverse(@sort_type); @w_sort_item = reverse(@sort_item);
「@sort_type」と「@sort_item」に入っている項目を逆順にします。入力データ全体をその都度並べ替えるので、これは優先度の低い項目から並べ替えをする必要があるためです。
ソート(並べ替え)キーの数だけ繰り返す
for ($k = 0; $k <= $#w_sort_type; $k++) { $n = $w_sort_item[$k];
並べ替えを行うために繰り返し処理を行います。
最初のfor文は「@w_sort_type」に指定した分だけ、並べ替えを繰り返すためのものです。このため、0番目から「@w_sort_type」の最終要素順である「$#w_sort_type」までを繰り返すように指定します。たとえば、「@w_sort_type」に3つの要素がある場合は、「2」が入ります。
また、並べ替えをする項目が何番目の項目かを「$n」に入れます。
ソート(並べ替え)処理1
if ($w_sort_type[$k] eq "CA") { #文字列型の昇順 for ($i = 0; $i <= $#in1_rec; $i++) { for ($j = 1; $j <= ($#in1_rec - $i); $j++) { $in1_key_a = (split("\t",$in1_rec[$j - 1]))[$n]; $in1_key_b = (split("\t",$in1_rec[$j]))[$n]; if ($in1_key_a gt $in1_key_b) { #逆順の時 ($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]); } } } }
その後に、並べ替えのタイプ別に挿入ソートのロジックで並べ替え処理を行います。挿入ソートですので、同じキーであれば入力順を保つことができます。2つのfor文で、たとえば、CSVファイルの行数が10行の場合、まず、1行目から10行目までを調べて、その中の最大値のある行を10行目に入れ、次に1行目から9行目までを調べてその中で最大値のある行を9行目に入れ・・・ということを繰り返して行きます。2番目のfor文は10行目、9行目・・・と変化していく最大値を入れる行をコントロールしています。
「$in1_key_a」と「$in1_key_b」には、隣り合ったレコードでの該当するソート(並べ替え)キーを格納し、ソート(並べ替え)キーが逆順の場合だけレコード全体を入れ替えます。
($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]);
上記のようにすることで、1行で隣り合ったレコードの入れ替えを行うことができます。
ソート(並べ替え)処理2
elsif ($w_sort_type[$k] eq "CD") { #文字列型の降順 for ($i = 0; $i <= $#in1_rec; $i++) { for ($j = 1; $j <= ($#in1_rec - $i); $j++) { $in1_key_a = (split("\t",$in1_rec[$j - 1]))[$n]; $in1_key_b = (split("\t",$in1_rec[$j]))[$n]; if ($in1_key_a lt $in1_key_b) { #逆順の時 ($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]); } } } } elsif ($w_sort_type[$k] eq "NA") { #数値型の昇順 for ($i = 0; $i <= $#in1_rec; $i++) { for ($j = 1; $j <= ($#in1_rec - $i); $j++) { $in1_key_a = (split("\t",$in1_rec[$j - 1]))[$n]; $in1_key_b = (split("\t",$in1_rec[$j]))[$n]; if ($in1_key_a > $in1_key_b) { #逆順の時 ($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]); } } } } elsif ($w_sort_type[$k] eq "ND") { #数値型の降順 for ($i = 0; $i <= $#in1_rec; $i++) { for ($j = 1; $j <= ($#in1_rec - $i); $j++) { $in1_key_a = (split("\t",$in1_rec[$j - 1]))[$n]; $in1_key_b = (split("\t",$in1_rec[$j]))[$n]; if ($in1_key_a < $in1_key_b) { #逆順の時 ($in1_rec[$j],$in1_rec[$j - 1]) = ($in1_rec[$j - 1],$in1_rec[$j]); } } } } }
文字列型の降順や数値型の昇順・降順についても同様な方法で並べ替えをしていきます。逆順になったときだけ各レコード単位で入れ替えを行います。
ソート(並べ替え)済ファイルの出力
# ソート(並べ替え)済ファイルの出力 foreach $record (@in1_rec) { print OUT1 "$record\n";
最後にソート(並べ替え)済みのファイルを出力します。
ccc,5555 aaa,7777 bbb,9999 aaa,1111 bbb,3333 bbb,4444 aaa,5555 ccc,1111
aaa,1111 ccc,1111 bbb,3333 bbb,4444 aaa,5555 ccc,5555 aaa,7777 bbb,9999