CSVデータファイルを行単位で処理するためのクラス。以下のような特徴を持つ。
C++で実装されており高速に動作する。
一行目が項目名行であれば、項目名をkeyとするHashにデータを格納できる。
データの格納はHash/Arrayを選択可能(Arrayの方が2倍高速)。
キーブレイク処理を容易に扱える。
RFC4180にほぼ準拠
一行の項目数は固定であることを前提とする。
* MCMD::Mcsvin::new(arguments){block}
Mcsvinオブジェクトを生成する。 argumentsに、以下の引数をスペースで区切った文字列として指定する。
i= |
入力ファイル名(String) 【必須】 |
k= |
キーブレイクを検知する項目リスト。複数項目はカンマで区切る。 |
キー指定の有無によってeachメソッドのyield引数が異なることに注意する。 |
|
-nfn |
1行目を項目名と見なさない。 |
-array |
eachメソッドで各データ項目をArrayに格納する。 |
指定がなければHashに格納する。 |
|
ArrayはHashに比べ、約2倍効率的である(詳細は「ベンチマーク」を参照)。 |
|
block |
ブロックがが指定されていれば実行(yield)する。 |
* MCMD::Mcsvin::each{|val| block}
* MCMD::Mcsvin::each{|val,top,bot| block}
CSVファイルを一行ずつ処理する。 1)の書式はキー(k=)を指定しなかった場合で、valに値が設定される。 2)の書式はキーを指定した場合で、val以外にもキーブレイク情報top,botの変数も設定される。
項目名をキーとして値を格納したHash(もしくはArray)。値は全てString型で格納。
k=で指定したキーの先頭であればtrue、さもなければfalseがセットされる。詳細は備考を参照。
k=で指定したキーの終端であればtrue、さもなければfalseがセットされる。詳細は備考を参照。
* MCMD::Mcsvin::names()
項目名配列を返す。-nfnが指定されていればnilを返す。
-nfnが指定された場合、データはArrayに格納される。Hashでは格納できない。
k=を指定する場合は、そこで指定した項目で並べ替えておかなければならない。
キーブレイクのロジック:
MCMD::Mcsvin.new("i=input.dat k=key"){|csv| csv.each{|val,top,bot| : } }
上記のコードにおいて、bool型のブロック変数topおよびbotの設定ロジックは以下のとおり。
データ行を、
行目のキー項目(“key”)の値を
とし、便宜上
とする。ただし、
である。
![]() |
(2.1) |
![]() |
(2.2) |
# dat1.csv 顧客,日付,金額 A,20081201,10 B,20081002,40 MCMD::Mcsvin.new("i=dat1.csv"){|csv| puts csv.names.join(",") csv.each{|val| p val } } # 出力結果 顧客,日付,金額 ["顧客"=>"A", "日付"=>"20081201", "金額"=>"10"] ["顧客"=>"B", "日付"=>"20081002", "金額"=>"40"]
# dat1.csv 顧客,日付 A,20081201 A,20081202 B,20081003 C,20081004 C,20081005 C,20081006 csv=MCMD::Mcsvin.new("i=dat1.csv k=顧客") csv.each{|val,top,bot| puts "#{val['顧客']},#{val['日付']} top=#{top} bot=#{bot}" } csv.close # 出力結果 A,20081201 top=true bot=false A,20081202 top=false bot=true B,20081003 top=true bot=true C,20081004 top=true bot=false C,20081005 top=false bot=false C,20081006 top=false bot=true
-nfnを指定するとArrayに格納される。
# dat1.csv A,20081201 A,20081202 MCMD::Mcsvin.new("i=dat1.csv k=1 -nfn"){|csv| puts csv.names # -> nil csv.each{|val| p val } } # 出力結果 nil ["A","20081201"] ["A","20081202"]
# dat1.csv 顧客,日付,金額 A,20081201,10 B,20081002,40 # -arrayオプションでArray格納 MCMD::Mcsvin.new("i=dat1.csv -array"){|csv| puts csv.names.join(",") csv.each{|val| p val } } # 出力結果 顧客,日付,金額 ["A", "20081201", "10"] ["B", "20081002", "40"]
Mcsvout : CSVデータの書き込み
Mtable : セル単位読み込み操作
CSVデータの読み込み処理について、Rubyの拡張ライブラリとして提供されている 各種ライブラリをベンチマークとして処理速度の比較を行う。 ベンチマーク対象は以下の4つのライブラリと1つのコマンドである。
http://raa.ruby-lang.org/project/csvscan/
http://tmtm.org/ruby/lightcsv/
http://www.gesource.jp/programming/ruby/database/fastercsv.html
http://www.ruby-lang.org/ja/old-man/html/CSV.html
項目を切り出すMコマンド(全てC++で実装)。参考までに掲載。
Table 2.1にベンチマークテストの結果を示す。 1万行〜500万行のデータについて読み込み実験を行った。 Figure 2.1には、ベンチマークテストで利用したスクリプトの抜粋が示されている。 McsvinはCSVScan(Cによる実装)とほぼ同等性能である。 その他はいずれもRubyネイティブコードによる実装なのでその差があらわれていることがわかる。 ただし、mcutとの比較においては、Mcsvinも到底及ばない。 mcutとMcsvinで採用しているCSVのparsingロジックおよびその実装は全く同じであるので、 データをArrayに格納するなどのRubyとのインターフェースにまつわるコストによって、 ここまでの差が出ていることになる。Figure 2.1に、ベンチマークテストで利用したスクリプトの抜粋を示す。
行数 |
10K |
100K |
1000K |
2000K |
3000K |
4000K |
5000K |
Mcsvin |
0.020 |
0.196 |
1.76 |
3.51 |
5.26 |
7.02 |
8.79 |
CSVScan |
0.021 |
0.187 |
1.83 |
3.67 |
5.50 |
7.33 |
9.17 |
LightCsv |
0.155 |
1.62 |
15.99 |
– |
– |
– |
– |
FasterCSV |
0.196 |
1.96 |
19.50 |
– |
– |
– |
– |
CSV |
1.44 |
14.3 |
– |
– |
– |
– |
– |
mcut |
– |
– |
0.095 |
0.177 |
0.260 |
0.342 |
0.423 |
require 'rubygems' require 'csv' require 'fastercsv' require 'lightcsv' require 'csvscan' require 'mcmd' require 'benchmark' puts Benchmark.measure{ (0...10).each{|i| # Mcsvinの場合 csv=MCMD::Mcsvin.new("i=data.csv -array"){|csv| csv.each{|val|}} # CSVScanの場合 File.open("data.csv","r"){|fp| CSVScan.scan(fp){|row|}} # LightCsvの場合 LightCsv.foreach("data.csv"){|row|} # FasterCSVの場合 FasterCSV.foreach("data.csv"){|row|} # CSVの場合 CSV.open("data.csv", 'r'){|row|} } }
次に、Mcsvinにおいて、キー指定の有無およびデータを格納する型による実行時間の差について見てみる(Table 2.2)。 キー指定の有無による速度には、さほど大きな違いはないが、データの格納型については、 ArrayがHashより2倍ほど効率的である。
キー |
型 |
1000K |
2000K |
3000K |
4000K |
5000K |
なし |
Array |
1.76 |
3.51 |
5.26 |
7.02 |
8.79 |
なし |
Hash |
3.52 |
6.99 |
10.50 |
14.00 |
17.52 |
あり |
Array |
1.97 |
3.92 |
5.88 |
7.84 |
9.83 |
あり |
Hash |
3.68 |
7.34 |
11.01 |
14.73 |
18.34 |