CSVファイルを文字列型や数値型に区別して、昇順にも降順にもソート(sort:並べ替え)することのできるスクリプトです。
数値型というのは、数字を計算するための数値として扱うということで、たとえば、昇順に並べ替えると「1,2,3,10,20,30」のような順番になりますが、文字列型では「1,10,2,20,3,30」のような順番になります。表計算ソフトなどでは、誤解しているケースが多いようですが、文字列型・数値型というのは、データそのものによって決まるのではなく、データをどのように扱う(認識する)かによって決まりますので、データが数字だからといって数値型と即断するのは誤りです(文字列型の可能性もあります)。たとえば、郵便番号や電話番号は(ハイフンは除くものとすると)すべて数字から成り立っていますが、通常は文字列型として扱います。一方、点数や長さ、重さなどは通常、数値型として扱います。また、ISBNコードやJANコードなどは通常は文字列型として扱いますが、チェックディジットを計算する場合は、各桁の数字を数値として扱います。
並べ替える項目が文字列型か数値型かによって指定する方法が変わります。
実際に応用する場合は、入出力データのファイル名を変更し、「$a[2] <=> $b[2]」の部分などを変更します。2つ以上のキーで並べ替えを行うには、「「$a[2] <=> $b[2]」の後に「or」をつけて指定していきます。この例では、2番目の項目を第1キーに、1番目の項目を第2キーとして並べ替えるように指定しています。Perlでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。
文字列型を昇順に並べ替えるのであれば、「$a[1] cmp $b[1]」のように、文字列型を降順に並べ替えるのであれば、$aと$bを入れ替えて「$b[1] cmp $a[1]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。
また、数値型を昇順に並べ替えるのであれば、「$a[1] <=> $b[1]」のように、数値型を降順に並べ替えるのであれば、$aと$bを入れ替えて「$b[1] <=> $a[1]」のように指定します。文字列型と異なる点は「cmp」とする部分を「<=>」とする点です。
なお、実用的な入力データの件数は指定するキーの数にもよりますが、ソート(sort:並べ替え)キーを2つ指定したときで、10万件程度までです。それ以上の入力件数がある場合は、実用にはなりません。
10万件以上のデータをソート(sort:並べ替え)する場合には、[2-4-5.大量のCSVファイルのソートする場合の考慮事項]と[2-4-7.大量のCSVファイルの文字列型・数値型/昇順・降順ソート]の両方を参考にして、スクリプトを作成してください(100万件程度のデータであれば、ソート(sort:並べ替え)できます)。
下記の例では、入力ファイルの各フィールドを「"」(ダブルクォーテーションつき)で囲み、それぞれのデータを「,」(カンマ)で区切っている形式を前提としているため、
$line1 =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($line1 =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
としていますが、入力ファイルの各フィールドを「"」(ダブルクォーテーションつき)で囲まずに、それぞれのデータを「,」(カンマ)で区切っている、いわゆる「CSV2形式」であれば、以下のようにすることができます。
@in1 = split(",",$line1,-1);
また、タブ区切りであれば、以下のようにすることができます。
@in1 = split("\t",$line1,-1);
# csvsort2.pl # 内容 : 対象ファイルをCSV形式としてソート(並べ替え)する # 文字列型の昇順・降順、数値型の昇順・降順の並べ替えを行う。 # (通貨記号や","つきの数字は数値型としての並べ替えはできない) # Copyright (c) 2009 Mitsuo Minagawa, All rights reserved. # (minagawa@fb3.so-net.ne.jp) # 使用方法 : c:\>perl csvsort2.pl # open(IN1,"input.txt"); open(OUT1,">output_s.txt"); # CSVファイルの入力 @record = (); $in1_ctr = 0; while ($line1 = <IN1>) { chomp($line1); $line1 =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/; @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($line1 =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g); $record[$in1_ctr] = join("\t",@in1); $in1_ctr++; } # CSVファイルの並べ替え # 文字列タイプの昇順:$a[1] cmp $b[1] #2番目の項目の昇順 # 文字列タイプの降順:$b[0] cmp $a[0] #1番目の項目の降順 # 数値タイプの昇順:$a[1] <=> $b[1] #2番目の項目の昇順 # 数値タイプの降順:$b[0] <=> $a[0] #1番目の項目の降順 @sort_rec = sort { my @a = split("\t", $a); my @b = split("\t", $b); $a[1] <=> $b[1] or $a[0] cmp $b[0]; } @record; # CSVファイルの出力 foreach $record (@sort_rec) { print OUT1 "$record\n"; } close(IN1); close(OUT1);
それでは、1つずつ解説していきましょう。
ソート(並べ替え)処理
@sort_rec = sort { my @a = split("\t", $a); my @b = split("\t", $b); $a[1] <=> $b[1] or $a[0] cmp $b[0]; } @record;
上記の「@record」には、並べ替えをする前のレコードが入っています。各項目をタブ区切りしたレコードを要素とする配列となっています。このレコードを「my @a = split("\t", $a);」でタブ区切りして、必要な項目で並べ替えられるようにします。「$a」と「$b」にはそれぞれ隣り合うレコードが自動的に入ります。この「$a」と「$b」の名前は変えることができません。
文字列型を昇順に並べ替えるのであれば、「$a[1] cmp $b[1]」のように、文字列型を降順に並べ替えるのであれば、$aと$bを入れ替えて「$b[1] cmp $a[1]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。Perlでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。
また、数値型を昇順に並べ替えるのであれば、「$a[1] <=> $b[1]」のように、数値型を降順に並べ替えるのであれば、$aと$bを入れ替えて「$b[1] <=> $a[1]」のように指定します。文字列型と異なる点は「cmp」とする部分を「<=>」とする点です。
ソート(並べ替え)のキーが1つの場合
なお、並べ替えの項目を指定するキーが1つであれば、以下のようになり、
@sort_rec = sort { my @a = split("\t", $a); my @b = split("\t", $b); $a[1] <=> $b[1]; } @record;
ソート(並べ替え)キーを指定する部分では「$a[1] <=> $b[1];」のように最後にセミコロンをつけます。
ソート(並べ替え)のキーが3つの場合
また、並べ替えの項目を指定するキーが3つであれば、
@sort_rec = sort { my @a = split("\t", $a); my @b = split("\t", $b); $a[1] <=> $b[1] or $a[0] cmp $b[0] or $a[2] cmp $b[2]; } @record;
となります。上記では、ソート(並べ替え)キーを指定する部分では「or $a[2] cmp $b[2];」のように最後にセミコロンをつけ、それ以外の「or $a[0] cmp $b[0]」には最後にセミコロンをつけませんので、この点に注意しください。
ソート(並べ替え)した結果の出力
foreach $record (@sort_rec) { print OUT1 "$record\n"; }
上記では、並べ替えが終わった後のレコードを出力しています。
「@sort_rec」には、並べ替えが終わった後のレコードが入っていますので、そのレコードをそのまま「foreach」で先頭からの順番に出力します。
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