MAGAZINE
ルーターマガジン
Ruby
ちょうどいいRuby CSVモジュールの使いどころ
2021.02.05
初めまして。12月からrooterの学生アルバイトとして働いておりますmiyayamaと申します。今回は、研修の中で学んだRubyのCSVモジュールの使い方について書きたいと思います。
Ruby標準添付ライブラリーであるcsvモジュールは、ruby上でCSVの入出力およびCSV形式のレコード作成まで面倒をみてくれます。しかし、全ての処理をcsvモジュールに頼るのが良いというと、そうでもありません。例えば、Ruby上の配列データをCSVファイルとして出力することを考えてみます。
Rubyで出力したcsvファイルは、UTF-8でコードされています。Excelは何も指定のないファイルは全てShift-JISでコードされたファイルとして開く設定になっているので、UTF-8でコードされたファイルは文字化けします。 そこで、BOMというものをファイルの先頭に追加する必要があります。
CSV.open (Ruby 2.7.0 リファレンスマニュアル)
これは、読み取り時にBOMを取り除くオプションを指定できるということなのですが、書き込み時にBOMを追加するオプションはCSV.openにはありません。紛らわしいですが注意しましょう。
Ruby標準添付ライブラリーであるcsvモジュールは、ruby上でCSVの入出力およびCSV形式のレコード作成まで面倒をみてくれます。しかし、全ての処理をcsvモジュールに頼るのが良いというと、そうでもありません。例えば、Ruby上の配列データをCSVファイルとして出力することを考えてみます。
CSVファイルの出力
require 'csv'
people = [["山田太郎", "男", "19"],["鈴木華子", "女", "25"],["田中一郎", "男", "32"]]
CSV.open("people.csv", "w", :force_quotes => true) do |file|
people.each do |person|
file << person
end
end
csvモジュールに含まれるCSV.openメソッド使って簡単に出力ができました。
しかしpeople.csvをExcelで開いてみると、
文字化けしてしまいました。
Rubyで出力したcsvファイルは、UTF-8でコードされています。Excelは何も指定のないファイルは全てShift-JISでコードされたファイルとして開く設定になっているので、UTF-8でコードされたファイルは文字化けします。 そこで、BOMというものをファイルの先頭に追加する必要があります。
BOM(Byte Order Mark)とは
Unicodeで書かれたファイルであることを明示するために、ファイルの先頭につける数バイトのデータのことです。UTF-8の場合は、"0xEF 0xBB 0xBF"を先頭に追加すると、BOMつきUTF-8としてファイルを出力でき、ExcelがUTF-8としてファイルを開いてくれます。BOMつきCSVファイルの出力
では、BOMを追加してみましょう。require 'csv'
people = [["山田太郎", "男", "19"],["鈴木華子", "女", "25"],["田中一郎", "男", "32"]]
bom ="\xEF\xBB\xBF" #bomを作成
CSV.open("people.csv", "w", :force_quotes => true) do |file|
file << bom #bomを先頭に追加?
people.each do |person|
file << person
end
end
しかしこれはできません。CSV.openメソッドは、ファイルをブロックの中でCSVクラスのオブジェクトという形で渡しています。CSVクラスのオブジェクトには、文字列であるBOMを追加することができないのでエラーが発生するのです。
そこで、ファイルの書き込みはcsvモジュールに頼らず、Rubyに組み込まれているIOクラスのFile.openメソッドを使って行ってみます。一方、配列をcsvへ変換する時には、csvモジュールに組み込まれたto_csvメソッドを使います。
require 'csv'
people = [["山田太郎", "男", "19"],["鈴木華子", "女", "25"],["田中一郎", "男", "32"]]
bom ="\xEF\xBB\xBF" #bomを作成
File.open("people.csv", "w") do |file|
file.print(bom) #bomを先頭に追加
people.each do |person|
file.puts(person.to_csv(:force_quotes => true))
end
end
出力ファイルをExcelで開いてみます。
文字化けされず表示されました!
CSV.generateメソッドを使ったやり方
実はcsvモジュール内のCSV.generateメソッドを用いてもBOMつきcsvを作ることができます。CSV.generateは、引数に与えた文字列を追加したCSVオブジェクトをブロックに渡します。文字列としてのcsvが返ってくるので、これをFile.writeメソッドで書き込みます。require 'csv'
people = [["山田太郎", "男", "19"],["鈴木華子", "女", "25"],["田中一郎", "男", "32"]]
bom ="\xEF\xBB\xBF" #bomを作成
csv_string = CSV.generate(bom, :force_quotes => true) do |csv| #bomをラップしてブロックに渡す
people.each do |person|
csv << person
end
end
File.open("test.csv", "w") do |file|
file.write(csv_string)
end
しかし、CSV.generateメソッドの使用には注意が必要です。
- Ruby2.5系でCSV.generateでラップした文字列が追加されないバグがあるhttps://bugs.ruby-lang.org/issues/14253
- CSV.generateはcsvの全行を含んだ文字列を作成するので、ビッグデータを扱うときはメモリの限界までしか展開できない
ちょうど良いCSVモジュールの使い方
このようにcsvモジュールは便利ですが、BOMを追加したい時など、柔軟性に欠けるところがあります。 他にも、CSV.openはファイルをブロック内で配列として開くので、putsなどの標準出力により要素を追加することはできませんが、IO.openであればputsメソッドなどの標準出力が使えます。 そのため、- CSVモジュールはCSVのレコードを作る範囲でのみ使う
- ファイルの入出力はRuby組み込みのIOクラスを使う
おまけ
CSV.openメソッドの公式リファレンスを見ると、BOMに関する記述があります。CSV.open (Ruby 2.7.0 リファレンスマニュアル)
これは、読み取り時にBOMを取り除くオプションを指定できるということなのですが、書き込み時にBOMを追加するオプションはCSV.openにはありません。紛らわしいですが注意しましょう。
CONTACT
お問い合わせ・ご依頼はこちらから