Edited at

【Ruby】よく使う、CSVライブラリを使ったCSV操作

More than 1 year has passed since last update.

Ruby標準ライブラリのcsvを使ったCSV操作で、個人的によく使うものを整理した。

次のfuture_gadgets.csvという名称のCSVファイルを例として使う。


future_gadgets.csv

number,name,function

1号機,ビット粒子砲,おもちゃの光線銃にテレビのリモコンを合体させたもの
2号機,タケコプカメラー,軸の部分に小型カメラを仕込んだ竹トンボ
3号機,もしかしてオラオラですかーっ!?,ウソ発見器と称した発汗検知装置

出典: https://ja.wikipedia.org/wiki/STEINS;GATEの用語一覧


CSVファイルから1行ずつ読み取りたい

CSV#foreachを使う。

1行は1つの配列として表現される。

require 'csv'

CSV.foreach('/path/to/future_gadgets.csv') do |fg|
# fgはArrayクラス
puts "[#{fg[0]}] #{fg[1]}: #{fg[2]}"
end

1行ずつ処理をするため、行数が多いCSVファイルを扱う場合でもメモリ使用量を気にしないで使える。


実行結果

[number] name: function

[1号機] ビット粒子砲: おもちゃの光線銃にテレビのリモコンを合体させたもの
[2号機] タケコプカメラー: 軸の部分に小型カメラを仕込んだ竹トンボ
[3号機] もしかしてオラオラですかーっ!?: ウソ発見器と称した発汗検知装置


CSVファイルから1度に読み込みたい

CSV#readを使う。

1行は1つの配列として表現され、全体として配列の配列が返り値となる。

require 'csv'

future_gadgets = CSV.read('/path/to/future_gadgets.csv')
future_gadgets.each do |fg|
# fgはArrayクラス
puts "[#{fg[0]}] #{fg[1]}: #{fg[2]}"
end


実行結果

[number] name: function

[1号機] ビット粒子砲: おもちゃの光線銃にテレビのリモコンを合体させたもの
[2号機] タケコプカメラー: 軸の部分に小型カメラを仕込んだ竹トンボ
[3号機] もしかしてオラオラですかーっ!?: ウソ発見器と称した発汗検知装置


CSV文字列から読み取りたい

ファイルではなく文字列としてのCSVを受け取りたい場合は、CSV#parseを使う。

CSV#foreachと同じく、ブロックには行が配列として渡される。

require 'csv'

CSV.parse('foo,bar,baz') do |fg|
puts fg[0] # => 'foo'
puts fg[1] # => 'bar'
puts fg[2] # => 'baz'
end


ヘッダのフィールド名をキーにして操作したい

CSV.foreachCSV.parseの第2引数にはHash形式でオプションを渡すことができ、ここにheaders: trueを与えることで、先頭行をヘッダして読み取ってくれるようになる。

これにより、ブロックに渡されるオブジェクトのクラスがArrayからCSV::Rowになる。

CSV::Rowはフィールド名をキーとして使えるようになるのでコードが読みやすくなる。

require 'csv'

CSV.foreach('/path/to/future_gadgets.csv', headers: true) do |fg|
# fgはCSV::Row
puts "[#{fg['number']}] #{fg['name']}: #{fg['function']}"
end


実行結果

[1号機] ビット粒子砲: おもちゃの光線銃にテレビのリモコンを合体させたもの

[2号機] タケコプカメラー: 軸の部分に小型カメラを仕込んだ竹トンボ
[3号機] もしかしてオラオラですかーっ!?: ウソ発見器と称した発汗検知装置


空行を読み飛ばしたい

skip_blanks: trueをオプションとして渡す。

指定しない場合空行は空の配列などになるが、このオプションを指定することでスキップできる。

require 'csv'

CSV.foreach('/path/to/future_gadgets.csv', skip_blanks: true) do |fg|
# ...
end

空行があるとエラーになってしまったり面倒なチェック処理が必要になる場合が多いので、とりあえず指定することが多い。


TSVを読み取りたい

RubyのCSVライブラリは、名前からCSVしか操作できないような印象を受けるがそうではない。

col_sepオプションで区切り文字を自由に設定できるので、TSVでも同じように扱うことができる。

require 'csv'

CSV.foreach('/path/to/future_gadgets.tsv', col_sep: "\t") do |fg|
# ...
end