大量のCSVファイルを文字列型のキーで昇順にソート(sort:並べ替え)するスクリプトです。
[2-4-5.大量のCSVファイルのソート(sort:並べ替え)する場合の考慮事項]でソート(sort:並べ替え)キーごとの累計件数から割り出したソート(sort:並べ替え)キーをもとに大量のCSVファイルを分割しながら並べ替えていきます。下記の例で"AAJJ"の箇所に分割して並べ替える際の基準になるソート(並べ替え)キーを指定していきます。このとき、「<=」や「<」で比較する場合には、ソート(sort:並べ替え)キーを小さい順に指定し、「>=」や「>」で比較する場合には、ソート(sort:並べ替え)キーを大きい順に指定します。
if (in1[0] <= "AAJJ")
sort_rec01[hash_key] = record
実際に応用する場合は、入出力データのファイル名を変更し、「sortitem = [0,1]」の部分を変更します。この例では、1番目の項目を第1キーに、2番目の項目を第2キーとして並べ替えるように指定しています。rubyでは、配列のインデックスは「0」から開始されますので、1番目の項目を「0」、2番目の項目を「1」として指定することに注意してください。
下記の例では、フィールドを「"」(ダブルクォーテーションつき)で囲み、それぞれのデータを「,」(カンマ)で区切っている形式を前提としているため、
in1 = (record + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
としていますが、フィールドを「"」(ダブルクォーテーションつき)で囲まずに、それぞれのデータを「,」(カンマ)で区切っている、いわゆる「CSV2形式」であれば、以下のようにすることができます。
in1 = line1.split(",",-1)
また、タブ区切りであれば、以下のようにすることができます。
in1 = line1.split("\t",-1)
# csvsort_many.rb
# 内容 :大量のCSVファイルを文字列型で昇順にソートする
# Copyright (c) 2011-2015 Mitsuo Minagawa, All rights reserved.
# (minagawa@fb3.so-net.ne.jp)
# 使用方法 : c:\>ruby csvsort_many.rb
#
# 入力ファイルを全部読み込む。
in1_file = IO.readlines("input.txt")
# 出力ファイル
out1_file = open("output.txt","w")
# 整列の基準となる項目番号
# ソートキー
sortitem = [0,1]
# ソート用のキー
hash_key = nil
# ソートするキーとレコードをセットするハッシュ
sort_rec01 = Hash.new
sort_rec02 = Hash.new
sort_rec03 = Hash.new
sort_rec04 = Hash.new
sort_rec05 = Hash.new
sort_rec06 = Hash.new
sort_rec07 = Hash.new
sort_rec08 = Hash.new
sort_rec09 = Hash.new
sort_rec10 = Hash.new
sort_rec11 = Hash.new
sort_rec12 = Hash.new
# ソートキーとなる項目番号からソートキーとなる文字列を作っておく
in1_file.each_with_index {|record,recno|
record.chomp!
# CSVファイルを分解し、配列にセットする
in1 = (record + ',')
.scan(/"([^"\\]*(?:\\.[^"\\]*)*)",|([^,]*),/)
.collect{|x,y| y || x.gsub(/(.)/, '\1')}
# タブ区切りファイルのときは下記の通り
# in1 = record.split("\t",-1)
# 整列の基準となる項目番号の項目内容をタブ値ではさんで連結してキーとする
hash_key = ""
sortitem.each {|i|
hash_key += in1[i] + "\t"
}
# 同じ値を持つキーが複数存在する場合に以下のように指定する。
hash_key += sprintf("%08d",recno)
# レコードをハッシュの値としてセットする。
if (in1[0] <= "AAJJ")
sort_rec01[hash_key] = record
elsif (in1[0] <= "AAVV")
sort_rec02[hash_key] = record
elsif (in1[0] <= "BBJJ")
sort_rec03[hash_key] = record
elsif (in1[0] <= "BBVV")
sort_rec04[hash_key] = record
elsif (in1[0] <= "CCJJ")
sort_rec05[hash_key] = record
elsif (in1[0] <= "CCVV")
sort_rec06[hash_key] = record
elsif (in1[0] <= "XXJJ")
sort_rec07[hash_key] = record
elsif (in1[0] <= "XXVV")
sort_rec08[hash_key] = record
elsif (in1[0] <= "YYJJ")
sort_rec09[hash_key] = record
elsif (in1[0] <= "YYVV")
sort_rec10[hash_key] = record
elsif (in1[0] <= "ZZJJ")
sort_rec11[hash_key] = record
elsif (in1[0] <= "ZZVV")
sort_rec12[hash_key] = record
end
hash_key = nil
}
# キー項目+行番号の配列を整列し、それにしたがってレコードを出力する
sort_rec01.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec02.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec03.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec04.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec05.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec06.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec07.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec08.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec09.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec10.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec11.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
sort_rec12.sort_by{|key| key }.each{|key,value|
out1_file.print value,"\n"
}
# ファイルのクローズ
out1_file.close
基本的には[2-4-2.CSVファイルの文字列型昇順ソート]を応用したスクリプトになっています。
入力データの振り分け
# レコードをハッシュの値としてセットする。
if (in1[0] <= "AAJJ")
sort_rec01[hash_key] = record
elsif (in1[0] <= "AAVV")
sort_rec02[hash_key] = record
elsif (in1[0] <= "BBJJ")
sort_rec03[hash_key] = record
elsif (in1[0] <= "BBVV")
sort_rec04[hash_key] = record
elsif (in1[0] <= "CCJJ")
sort_rec05[hash_key] = record
elsif (in1[0] <= "CCVV")
sort_rec06[hash_key] = record
elsif (in1[0] <= "XXJJ")
sort_rec07[hash_key] = record
elsif (in1[0] <= "XXVV")
sort_rec08[hash_key] = record
elsif (in1[0] <= "YYJJ")
sort_rec09[hash_key] = record
elsif (in1[0] <= "YYVV")
sort_rec10[hash_key] = record
elsif (in1[0] <= "ZZJJ")
sort_rec11[hash_key] = record
elsif (in1[0] <= "ZZVV")
sort_rec12[hash_key] = record
end
[2-4-5.大量のCSVファイルのソート(並べ替え)する場合の考慮事項]でソート(並べ替え)キーごとの累計件数から各々100万件程度になるように割り出したソート(並べ替え)キー(原則として第1キー)を指定していきます。入力ファイルの件数によって、elsifの数を調整していきます。また、必要に応じてif文やelsif文には、第2キー以降をand条件で追加していきます。ここでセットした「sort_recxx(xxには01から始まる数字が入る)」の順に並べ替えをして出力していきます。
並べ替える件数にあわせて、上記の部分および「sort_recxx.sort_by・・・」で実際に出力している箇所を増減していきます。
同一のソート(並べ替え)キーで100万件を大幅に超える件数がある場合
また、同一のソート(並べ替え)キーで100万件を大幅に超える件数がある場合は、以下のように変更します。
まず、ハッシュの初期値設定をしている箇所ですが、
sort_rec01 = Hash.new
上記の箇所については、下記のようにハッシュではなく、通常の配列に変更します。
sort_rec01 = Array.new
また、条件ごとに振り分けている箇所ですが、
if (in1[0] <= "AAJJ")
sort_rec01[hash_key] = record
上記の箇所については、ハッシュキーではなく、以下のようにします。
if (in1[0] <= "AAJJ")
sort_rec01[recno] = record
さらに、ハッシュからソート(並べ替え)して出力する以下の箇所については、
sort_rec01.sort_by{|key,value| key}.each{|key,value|
out1_file.print value,"\n"
}
以下のように配列「sort_rec01」から順番に出力するように変更します。
sort_rec01.each {|record|
out1_file.print record,"\n"
}