大量のCSVファイルを文字列型や数値型に区別して、昇順にも降順にも並べ替えることのできるスクリプトです。
[2-4-5.大量のCSVファイルのソートする場合の考慮事項]でソート(sort:並べ替え)キーごとの累計件数から割り出したソート(sort:並べ替え)キーをもとに大量のCSVファイルを分割しながら並べ替えていきます。下記の例で"01203"の箇所に分割して並べ替える際の基準になるソートキーを指定していきます。このとき、「le」や「lt」で比較する場合には、ソート(sort:並べ替え)キーを小さい順に指定し、「ge」や「gt」で比較する場合(並べ替え)には、ソート(sort:並べ替え)キーを大きい順に指定します。
if ($in1[1] le "01203") {
$in1_rec01[$in1_ctr01] = join("\t",@in1);
$in1_ctr01++;
}
並べ替える項目が文字列型か数値型かによって指定する方法が変わります。
実際に応用する場合は、入出力データのファイル名を変更し、「$a[1] <=> $b[1]」の部分などを変更します。2つ以上のキーで並べ替えを行うには、「「$a[1] <=> $b[1]」の後に「or」をつけて指定していきます。この例では、2番目の項目を第1キーに、1番目の項目を第2キー、4番目の項目を第3キー、3番目の項目を第4キーとして並べ替えるように指定しています。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」とする部分を「<=>」とする点です。
下記の例では、フィールドを「"」(ダブルクォーテーションつき)で囲み、それぞれのデータを「,」(カンマ)で区切っている形式を前提としているため、
$tmp = $line1;
$tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/;
@in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_}
($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
としていますが、フィールドを「"」(ダブルクォーテーションつき)で囲まずに、それぞれのデータを「,」(カンマ)で区切っている、いわゆる「CSV2形式」であれば、以下のようにすることができます。
@in1 = split(",",$line1,-1);
また、タブ区切りであれば、以下のようにすることができます。
@in1 = split("\t",$line1,-1);
# csvsort2_many.pl
# 内容 : 大量のCSVファイルをソート(並べ替え)する
# 文字列型の昇順・降順、数値型の昇順・降順の並べ替えを行う。
# (通貨記号や","つきの数字は数値型としての並べ替えはできない)
# Copyright (c) 2011 Mitsuo Minagawa, All rights reserved.
# (minagawa@fb3.so-net.ne.jp)
# 使用方法 : c:\>perl csvsort2_many.pl
#
open(IN1,"input.txt");
open(OUT1,">output.txt");
# 分割してソート(並べ替え)するレコードを入れる配列
@in1_rec01 = ();
@in1_rec02 = ();
@in1_rec03 = ();
@in1_rec04 = ();
@in1_rec05 = ();
@in1_rec06 = ();
@in1_rec07 = ();
@in1_rec08 = ();
@in1_rec09 = ();
@in1_rec10 = ();
# 分割してソート(並べ替え)するレコードの件数
$in1_ctr01 = 0;
$in1_ctr02 = 0;
$in1_ctr03 = 0;
$in1_ctr04 = 0;
$in1_ctr05 = 0;
$in1_ctr06 = 0;
$in1_ctr07 = 0;
$in1_ctr08 = 0;
$in1_ctr09 = 0;
$in1_ctr10 = 0;
# CSVファイルの入力
while ($line1 = <IN1>) {
chomp($line1);
# $tmp = $line1;
# $tmp =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/;
# @in1 = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_}
# ($tmp =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
@in1 = split("\t",$line1,-1);
if ($in1[1] le "01203") {
$in1_rec01[$in1_ctr01] = join("\t",@in1);
$in1_ctr01++;
}
elsif ($in1[1] le "01210") {
$in1_rec02[$in1_ctr02] = join("\t",@in1);
$in1_ctr02++;
}
elsif ($in1[1] le "01223") {
$in1_rec03[$in1_ctr03] = join("\t",@in1);
$in1_ctr03++;
}
elsif ($in1[1] le "01346") {
$in1_rec04[$in1_ctr04] = join("\t",@in1);
$in1_ctr04++;
}
elsif ($in1[1] le "01453") {
$in1_rec05[$in1_ctr05] = join("\t",@in1);
$in1_ctr05++;
}
elsif ($in1[1] le "01547") {
$in1_rec06[$in1_ctr06] = join("\t",@in1);
$in1_ctr06++;
}
elsif ($in1[1] le "01636") {
$in1_rec07[$in1_ctr07] = join("\t",@in1);
$in1_ctr07++;
}
elsif ($in1[1] le "01691") {
$in1_rec08[$in1_ctr08] = join("\t",@in1);
$in1_ctr08++;
}
elsif ($in1[1] le "02205") {
$in1_rec09[$in1_ctr09] = join("\t",@in1);
$in1_ctr09++;
}
elsif ($in1[1] le "02401") {
$in1_rec10[$in1_ctr10] = join("\t",@in1);
$in1_ctr10++;
}
}
# CSVファイルの並べ替え
# 文字列タイプの昇順:$a[1] cmp $b[1] #2番目の項目の昇順
# 文字列タイプの降順:$b[0] cmp $a[0] #1番目の項目の降順
# 数値タイプの昇順:$a[1] <=> $b[1] #2番目の項目の昇順
# 数値タイプの降順:$b[0] <=> $a[0] #1番目の項目の降順
#
@sort_rec01 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec01;
@sort_rec02 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec02;
@sort_rec03 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec03;
@sort_rec04 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec04;
@sort_rec05 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec05;
@sort_rec06 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec06;
@sort_rec07 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec07;
@sort_rec08 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec08;
@sort_rec09 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec09;
@sort_rec10 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec10;
# CSVファイルの出力
foreach $record (@sort_rec01) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec02) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec03) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec04) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec05) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec06) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec07) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec08) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec09) {
print OUT1 "$record\n";
}
foreach $record (@sort_rec10) {
print OUT1 "$record\n";
}
close(IN1);
close(OUT1);
基本的には[2-4-3.CSVファイルの文字列型・数値型/昇順・降順ソート(その1)]を応用したスクリプトになっています。
入力データの振り分け
if ($in1[1] le "01203") {
$in1_rec01[$in1_ctr01] = join("\t",@in1);
$in1_ctr01++;
}
elsif ($in1[1] le "01210") {
$in1_rec02[$in1_ctr02] = join("\t",@in1);
$in1_ctr02++;
}
elsif ($in1[1] le "01223") {
$in1_rec03[$in1_ctr03] = join("\t",@in1);
$in1_ctr03++;
}
elsif ($in1[1] le "01346") {
$in1_rec04[$in1_ctr04] = join("\t",@in1);
$in1_ctr04++;
}
elsif ($in1[1] le "01453") {
$in1_rec05[$in1_ctr05] = join("\t",@in1);
$in1_ctr05++;
}
elsif ($in1[1] le "01547") {
$in1_rec06[$in1_ctr06] = join("\t",@in1);
$in1_ctr06++;
}
elsif ($in1[1] le "01636") {
$in1_rec07[$in1_ctr07] = join("\t",@in1);
$in1_ctr07++;
}
elsif ($in1[1] le "01691") {
$in1_rec08[$in1_ctr08] = join("\t",@in1);
$in1_ctr08++;
}
elsif ($in1[1] le "02205") {
$in1_rec09[$in1_ctr09] = join("\t",@in1);
$in1_ctr09++;
}
elsif ($in1[1] le "02401") {
$in1_rec10[$in1_ctr10] = join("\t",@in1);
$in1_ctr10++;
}
[2-4-5.大量のCSVファイルのソートする場合の考慮事項]でソート(並べ替え)キーごとの累計件数から各々10万件程度になるように割り出したソート(並べ替え)キー(原則として第1キー)を指定していきます。入力ファイルの件数によって、elsifの数を調整していきます。また、必要に応じてif文やelsif文には、第2キー以降をand条件で追加していきます。ここでセットした「$in1_recxx(xxには01から始まる数字が入る)」の順に並べ替えをして出力していきます。
ソート(並べ替え)処理
@sort_rec01 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec01;
上記の「@in1_recxx(xxには01から始まる数字が入る)」には、並べ替えをする前のレコードが入っています。各項目をタブ区切りしたレコードを要素とする配列となっています。このレコードを「my @a = split("\t", $a);」でタブ区切りして、必要な項目で並べ替えられるようにします。「$a」と「$b」にはそれぞれ隣り合うレコードがタブ区切りにした各項目が自動的に入ります。この「$a」と「$b」の名前は変えることができません。
並べ替える件数にあわせて、上記の部分および「foreach $record (@sort_recxx)・・・」で実際に出力している箇所を増減していきます。
文字列型を昇順に並べ替えるのであれば、「$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」とする部分を「<=>」とする点です。
並べ替える項目を追加したり、削除したりしたときは、並べ替える項目の最期、つまりor条件でつないだ最期の箇所に「;(セミコロン)」を忘れずに付けてください。別な場所につけたりするとエラーになり、実行できなくなります。
同一のソート(並べ替え)キーで10万件を大幅に超える件数がある場合
また、同一のソート(並べ替え)キーで10万件を大幅に超える件数がある場合は、該当する以下の部分をコメントにします。
@sort_rec01 = sort {
my @a = split("\t", $a);
my @b = split("\t", $b);
$a[1] <=> $b[1]
or $a[0] cmp $b[0]
or $a[3] cmp $b[3]
or $a[2] cmp $b[2];
}
@in1_rec01;
さらに、下記の箇所については、ソート(並べ替え)せずに、
foreach $record (@sort_rec01) {
print OUT1 "$record\n";
}
以下のように順番に出力するように変更します。
foreach $i (0..$#in1_rec01) {
print OUT1 "$in1_rec01[$i]\n";
}