LoginSignup
26

More than 5 years have passed since last update.

RubyのCSV処理で気をつけるポイント

Last updated at Posted at 2017-12-06

トレタ Advent Calendar 2017の7日目記事です。
サービスをスタートして4年がたち、初期のころにつくったCSV処理のコードを最近いくつか修正しました。そのポイントについてまとめようと思います。

こんなコードを

CSV.parse(NKF.nkf('-w', params[:file].read)).each do |i|
  process(i)
end

こういうふうにするようにしました。

open(params[:file].tempfile, 'rt:UTF-16:UTF-8', undef: :replace, invalid: :replace, replace: '') do |f|
  CSV.new(f, col_sep: "\t").each do |line|
    process(line)
  end
end

ポイント1: 全データをメモリに載せない

  • CSV.parse/CSV.read/CSV.tableあたりは全データがメモリにのってしまうので使わないようにします。
  • その点ではCSV.foreachもよいですが、ポイント2と3の文字コードや改行コードを考えるとCSV.newのほうが都合がいいです。
  • Rails等でファイルアップロードのデータを受け取るとき場合には、params[:file].tempfileを使えばファイルから読みだすことができて全データがメモリに載るのをさけることができます。
  • ふつうにファイルからデータを読めばいいケースではたんにファイル名を指定します。

ポイント2: 文字コード

  • NKF.nkfはstringを入力にとるのでポイント1を考えるとつかいにくいので、Encodingで変換します。
  • コード上はUTF-16のデータをうけとるようになっていますが、要件に合わせて変更します。この例でUTF-16を使っている理由はポイント4で説明します。
  • NKFと違ってEncodingの場合は変換時にundefやinvalidの例外が起こるでその場合の処理を指定します。(UTF-16からUTF-8であれば必要ないかも)

ポイント3: 改行コード

  • CSVライブラリはカラムのなかに複数の改行コードが混じっているとCSV::MalformedCSVError: Unquoted fields do not allow \r or \nがおきます。
  • これはrow_sepオプションで区切りの改行文字を指定することで回避できますが、そもそも複数種類の改行のデータを持っていいても困ることのほうが多いので、\nに正規化します。
  • Encodingの指定でrtを指定するか、universal_newlineオプションを使うと\nに正規化することができます。

ポイント4: Excelからデータを想定する

  • 残念ながら世の中の表データの大半はExcel取り扱われています。
  • Excelを使っているひとにCSVでくださいというと、たいていの場合Shift_JIS(CP932)のデータきてしまいます。
  • これはExcelでCSV書き出しをするとそうなるからで、UTF-8のCSVでほしい思っていてもなかなかそういうわけにはいきません。
  • また、Shift_JISで書き出されてしまうと、元データにShift_JISの範囲外の文字が入っていた場合にそれが失われていて、再変換したところで元データは手にはいりません。
  • こういうことを避けるために、ExcelではCSVではなく、Unicodeテキストという形式で保存してデータをもらうようにします。
  • UnicodeテキストのデータはUTF-16のTSV(タブ区切り)なので、それを受け取ることを想定して書いておきます。

今回、CSVを書き出すケースについては書いてませんが、同じように気をつけたほうがいい思います。あらかじめこういう点を考慮したコード書くのは手間ですが、あとあと文字コードやメモリ消費量でトラブルになる可能性をを考えると、はじめから少し手間をかけたほうがいいかなと思っています。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26