RubyでCSVファイルを読み取って解析する方法【基本から応用まで】
CSVとは「Comma-Separated Values(カンマ区切り値)」の略称です。
カンマで区切った値を1行ずつ並べた、広く使われているデータ形式で、データのインポートやエクスポートに活用されています。
例えば、Gmailの連絡先はCSVファイルとしてエクスポートでき、同じ形式でインポートすることも可能です。
CSVファイルは次のような構造になっています:
id,name 1,chocolate 2,bacon 3,apple 4,banana 5,almonds
この記事では、RubyのCSVライブラリを使ってCSVファイルを読み書きする方法を解説します。
RubyでのCSV解析の基本
Rubyには標準でCSVライブラリが組み込まれており、追加のインストールなしですぐに使えます。
ファイルを直接読み込む場合は以下のように書きます:
require 'csv'
CSV.read("favorite_foods.csv")
また、CSV形式の文字列を解析することもできます:
require 'csv'
CSV.parse("1,chocolate\n2,bacon\n3,apple")
実行結果はどうなるでしょうか?
答えは二次元配列です。配列の各要素が、表の1行に対応します。
具体的にはこのような出力になります:
[ ["id", "name"], ["1", "chocolate"], ["2", "bacon"], ["3", "apple"], ["4", "banana"], ["5", "almonds"] ]
data[1][1]のような配列のインデックスを使ってデータを操作できますが、実はもっと良い方法があります!
CSVオプション:ヘッダーの扱い方
ファイルにヘッダー行が含まれている場合は、CSVパーサーにその旨を伝えることができます。
書き方はこちら:
table = CSV.parse(File.read("cats.csv"), headers: true)
こうすると、多次元配列の代わりにCSV::Tableオブジェクトが返されます。
公式ドキュメントでは次のように説明されています:
「
CSV::TableはCSVドキュメントを表す二次元データ構造です。テーブルを使うと、行単位・列単位でデータを扱ったり、内容を加工したり、結果を再びCSV形式に変換して戻すこともできます。」
テーブルがあれば、任意の行から必要なデータを簡単に取り出せます。
例:
table[0]["id"] # "1" table[0]["name"] # "chocolate"
ここで0は最初の行、idとnameは列名を表しています。
テーブルには2つのモードがあります:
- by_col(列単位)
- by_row(行単位)
テーブルのモード(デフォルトはrow)を切り替えることで、同じデータを異なる視点から操作できます。
例:
table.by_col[0] # ["1", "2", "3", "4", "5"] table.by_col[1] # ["chocolate", "bacon", "apple", "banana", "almonds"]
ここで0は1列目、1は2列目を意味します。
なお、これらのメソッドはテーブルのコピーを返します。
元のテーブル自体を変更したい場合は、by_col!やby_row!メソッドを使用しましょう。コピーが作られないため、メモリ効率も良くなります。
CSVコンバーターの使い方
先ほどの出力で、id列が文字列の配列として取得されたことに気づいたかもしれません。
整数(Integer)として扱いたい場合はどうすれば良いのでしょうか?
各文字列にto_iを呼び出して変換することもできますが…
実はもっと便利な近道があります!
RubyのCSVライブラリにはコンバーター(converters)という仕組みが備わっています。
コンバーターを使うと、値を自動的に変換してくれます。
例:
CSV.parse("1,2,3,4,5")
# [["1", "2", "3", "4", "5"]]
CSV.parse("1,2,3,4,5", converters: :numeric)
# [[1, 2, 3, 4, 5]]
組み込みのコンバーターは6種類あります:
- Integer(整数)
- Float(浮動小数点数)
- Numeric(Float + Integer)
- Date(日付)
- DateTime(日時)
- All(すべて)
さらに、独自のカスタムコンバーターを作ることもできます。
作り方はこちら:
CSV::Converters[:symbol] = ->(value) { value.to_sym rescue value }
作成したコンバーターは次のように使います:
CSV.parse("a,b,c", headers: false, converters: :symbol)
# [[:a, :b, :c]]
新しいCSVファイルの作成方法
CSVファイルの解析や読み込みだけでなく、ゼロからCSVデータを作成することもできます。
シンプルな方法はこちら:
cats = [
[:blue, 1],
[:white, 2],
[:black_and_white, 3]
]
cats.map { |c| c.join(",") }.join("\n")
generateメソッドを使うこともできます:
CSV.generate do |csv| csv << [:blue, 1] csv << [:white, 2] csv << [:black_and_white, 3] end
これにより、正しいCSV形式のデータが準備されます。
ファイルに書き出したい場合は、File.write("cats.csv", data)を使うか、generateの代わりにファイル名と書き込みモードを指定したopenメソッドを使用します。
例:
CSV.open("cats.csv", "w") do |csv|
csv << [:white, 2]
end
これで新しいCSVファイルの完成です!
CSV用Gemとパフォーマンス比較
標準ライブラリだけでも十分実用的ですが、異なる機能を持つCSV解析用のGemもいくつか存在します。
例えば、smarter_csvというGemを使うと、CSVデータをハッシュの配列に変換できます。
例:
require 'smarter_csv'
IntegerConverter = Object.new
def IntegerConverter.convert(value)
Integer(value)
end
SmarterCSV.process('testing.csv', value_converters: { id: IntegerConverter })
# [{:id=>1, :name=>"a"}, {:id=>2, :name=>"b"}, {:id=>3, :name=>"c"}]
パフォーマンス比較の結果は以下の通りです:
Comparison:
CSV: 112.9 i/s
Smarter CSV: 21.7 i/s - 5.21x slower
Tabular: 17.3 i/s - 6.52x slower
この結果から、標準のCSVライブラリが他のGemよりも大幅に高速であることが分かります。
まとめ
この記事では、RubyでCSVファイルを読み書きする方法を学びました。コンバーターによる型変換や、CSVデータを処理するための代替Gemについても理解できましたね。
大きなCSVファイル(10MB超)を処理したい場合は、ブロック付きのCSV.foreach(file_name)メソッドの使用をおすすめします。1行ずつ読み込むため、メモリ消費を大幅に抑えられます。
この記事が役に立ったら、ぜひ周りの人にもシェアしてください!
-
Rubyでパーサーを自作する方法!StringScannerを使った実装手順を徹底解説
パース(構文解析)とは、文字列の集まりから意味を読み取り、プログラムが扱える形のデータへと変換する技術です。正規表現でも文字列の解析は可能ですが、すべての場面に適しているわけではありません。 たとえば、正規表現でHTMLを解析するのはあまり良い方法ではないというのは、プログラミング界隈ではよく知られた話です。 Rubyにはnokogiriという強力なライブラリがあり、HTMLの解析はこれに任せられます。しかし、自分でパーサーを一から作ってみると、文字列処理や構文解析の仕組みについて多くのことを学べます。それでは早速始めていきましょう! Rubyでのパースの基本:StringScannerクラス
-
【初心者向け】OneDriveでファイルを他のユーザーと共有する方法|共有の停止・権限変更も解説
Microsoft OneDriveは、Windowsに標準搭載されているオンラインストレージ兼同期サービスです。豊富な機能を備えており、競合サービスと比較しても引けを取らない、むしろ優れた使い勝手を提供しています。 OneDriveではドキュメントをオンライン上に保存でき、Web版のMicrosoft Office(Word、PowerPoint、Excel)にもブラウザから直接アクセスできます。さらに、組織外のパートナー、サプライヤー、顧客などとも安全に共同作業が可能で、相手がMicrosoft 365を契約しているかどうかも問いません。 OneDriveでのファイル共有はとても簡単ですが