CSVファイルを文字列型や数値型に区別して、昇順にも降順にもソート(sort:並べ替え)することのできるスクリプトです。
数値型というのは、数字を計算するための数値として扱うということで、たとえば、昇順に並べ替えると「1,2,3,10,20,30」のような順番になりますが、文字列型では「1,10,2,20,3,30」のような順番になります。表計算ソフトなどでは、誤解しているケースが多いようですが、文字列型・数値型というのは、データそのものによって決まるのではなく、データをどのように扱う(認識する)かによって決まりますので、データが数字だからといって数値型と即断するのは誤りです(文字列型の可能性もあります)。たとえば、郵便番号や電話番号は(ハイフンは除くものとすると)すべて数字から成り立っていますが、通常は文字列型として扱います。一方、点数や長さ、重さなどは通常、数値型として扱います。また、ISBNコードやJANコードなどは通常は文字列型として扱いますが、チェックディジットを計算する場合は、各桁の数字を数値として扱います。
並べ替える項目が文字列型か数値型かによって指定する方法が変わります。
実際に応用する場合は、入出力データのファイル名を変更し、下記の「[w_array[1].to_f,w_array[0]]」の部分などを変更します
sort_rec = in1_rec.sort_by{|data|
w_array = data.split("\t",-1)
[w_array[1].to_f,w_array[0]]
}
この例では、2番目の項目を第1キーで数値項目として、1番目の項目を第2キーで文字列項目として並べ替えるように指定しています。Rubyでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。
文字列型を昇順に並べ替えるのであれば、「w_array[0]」のように、文字列型を降順に並べ替えるのであれば、マイナスをつけて「-w_array[0]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。
また、数値型を昇順に並べ替えるのであれば、整数として並べ替える場合、「w_array[0].to_i」のように、整数として降順に並べ替えるのであれば、マイナスをつけて「-w_array[0].to_i」のように指定します。実数として並べ替えるのであれば、それぞれ「to_i」の部分を「to_f」に変更します。
なお、実用的な入力データの件数は指定するキーの数にもよりますが、ソート(並べ替え)キーを2つ指定したときで、10万件程度までです。それ以上の入力件数がある場合は、実用にはなりません。
下記の例では、入力ファイルの各フィールドを「"」(ダブルクォーテーションつき)で囲み、それぞれのデータを「,」(カンマ)で区切っている形式を前提としているため、
in1 = (line1 + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
としていますが、入力ファイルの各フィールドを「"」(ダブルクォーテーションつき)で囲まずに、それぞれのデータを「,」(カンマ)で区切っている、いわゆる「CSV2形式」であれば、以下のようにすることができます。
in1 = line1.split(",",-1)
また、タブ区切りであれば、以下のようにすることができます。
in1 = line1.split("\t",-1)
# csvsort2.rb
# 内容 : 対象ファイルをCSV形式としてソートする
# 文字列型の昇順・降順、数値型の昇順・降順の並べ替えを行う。
# (通貨記号や","つきの数字は数値型としての並べ替えはできない)
# Copyright (c) 2009-2015 Mitsuo Minagawa, All rights reserved.
# (minagawa@fb3.so-net.ne.jp)
# 使用方法 : c:\>ruby csvsort2.rb
#
# 入力ファイル
in1_file = open("input.txt","r")
# 出力ファイル
out1_file = open("output.txt","w")
in1_ctr = 0 #入力件数
in1_rec = Array.new #入力ファイルを格納する配列
sort_rec = Array.new #ソート済ファイルを格納する配列
# CSVファイルの入力
while (line1 = in1_file.gets)
line1.chomp!
in1 = (line1 + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
in1_rec[in1_ctr] = in1.join("\t")
#入力件数加算
in1_ctr += 1
end
# タブ区切りファイルのときは、以下のようにする
# 後でタブ区切りで配列に入れるので、この時点でCSVファイルのように配列にする必要はない
#while (line1 = in1_file.gets)
# line1.chomp!
# in1_rec[in1_ctr] = line1
#入力件数加算
# in1_ctr += 1
#end
# CSVファイルの並べ替え
# 文字列型の昇順(1番目の項目):w_array[0]
# 文字列型の降順(2番目の項目):-w_array[1]
# 整数型の昇順(3番目の項目):w_array[2].to_i
# 整数型の降順(4番目の項目):-w_array[3].to_i
# 実数型の昇順(3番目の項目):w_array[2].to_f
# 実数型の降順(4番目の項目):-w_array[3].to_f
# 複数項目の並べ替えは並べ替えをするキーの順番にカンマで区切って並べていく。
sort_rec = in1_rec.sort_by{|data|
w_array = data.split("\t",-1)
[w_array[1].to_f,w_array[0]]
}
sort_rec.each {|data|
out1_file.print data,"\n"
}
# ファイルのクローズ
in1_file.close
out1_file.close
それでは、1つずつ解説していきましょう。
ソート(並べ替え)処理
sort_rec = in1_rec.sort_by{|data|
w_array = data.split("\t",-1)
[w_array[1].to_f,w_array[0]]
}
「in1_rec」には、並べ替えをする前のレコードが入っています。各項目をタブ区切りした文字列となっています。この文字列を「w_array = data.split("\t",-1)」でタブ区切りして、配列にし、必要な項目で並べ替えられるようにします。この「w_array」の名前は変更可能です。
文字列型を昇順に並べ替えるのであれば、「w_array[0]」のように、文字列型を降順に並べ替えるのであれば、マイナスをつけて「-w_array[0]」のように指定します。[]の中には何番目の項目を並べ替えるのかを指定します。Rubyでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。
また、数値型を昇順に並べ替えるのであれば、整数として並べ替える場合、「w_array[0].to_i」のように、整数として降順に並べ替えるのであれば、マイナスをつけて「-w_array[0].to_i」のように指定します。実数として並べ替えるのであれば、それぞれ「to_i」の部分を「to_f」に変更します。
ソート(並べ替え)した結果の出力
sort_rec.each {|data|
out1_file.print data,"\n"
}
並べ替えが終わった後のレコードを出力しています。
「sort_rec」には、並べ替えが終わった後のレコードが入っていますので、そのレコードをそのまま「each」で先頭からの順番に出力します。
(注)なお、この方法でソート(並べ替え)すると、安定したソート(ソート(並べ替え)したキーが同じ場合、入力順に並ぶこと)にはなりません。安定したソートにしたい場合は、入力順に連番をつけて、その連番もソート(並べ替え)キーに含めることにより、行いますが、具体的には該当部分を以下のように変更します。
安定したソート(並べ替え)にするための方法
sort_rec = in1_rec.sort_by{|data|
i = i + 1
w_array = data.split("\t",-1)
[w_array[1].to_f,w_array[0],i]
}
sort_rec.each {|data|
w_array = data.split("\t",-1)
w_array.pop
out1 = w_array.join("\t")
out1_file.print out1,"\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