JeffreyFrancesco.org 公開日 タグ Tag Permalink 現在地 Facebook page Twitter RSS feed

仕事で 2,000 行くらいある CSV 形式のファイル(がさらに数ファイルある)の中身をチェックして、特定の URL が含まれていたらそのフィールドを空白にする…という作業をする必要があった。最初は LibreOffice Calc で一気に検索 & 置換すればいけるやろ、と軽く考えて試してみたら、これが軽く数 10 分は待たされるほど遅くて耐え切れない。

内容が改行を含むフィールドがあるので、少し複雑(というほどのものでもないけど)な正規表現を書いてたのも原因かもしれないが、かといってさすがにそこを単純な文字列検索にして、見つけるごとに手作業で消していくのは面倒くさい。あと、これらのファイルには他の置換作業も同時にいくつか必要だったので、そのたびに数分待たされるのもつらい。

そこで、以前から便利という話は聞いていたものの、これまで実は試したことのなかった Ruby の CSV クラスを使ったスクリプトを書いて処理させてみることにした。

# 適当な名前(例: csv.rb)で保存して、
# `ruby csv.rb foo.csv` などと実行すれば、
# 処理した foo-done.csv が同階層に出てくる。
require "csv"

source_file = ARGV[0]
(fn, ext) = source_file.split(/\./)
processed_file = "#{fn}-done.#{ext}"

# 元の CSV ファイルが Windows 形式なので。
# UTF-8 への変換は不要だったかもしれないけど、
# 元のまま処理するのは(昔の記憶で)抵抗があった…
read_opts = {
  encoding: "SJIS:UTF-8",
  headers: true,
  return_headers: true,
}
write_opts = {
  encoding: "SJIS",
  row_sep: "\r\n"
}

# あとは一気に読み出して処理しつつ書き込み。
CSV.open(processed_file, "w", write_opts) do |csv|
  CSV.foreach(source_file, read_opts) do |row|
    row.each do |k, v|
      if v.match(/\.example\.com/)
        row[k] = ""
      end
      # その他の置換処理がいくつか続く…
    end
    csv << row
  end
end

実際使ったものそのままではないけど、大筋はこんな感じ。自分さえ使えればいいので、細かいチェックやエラー処理は入れてない。もっとうまい書き方があるのかもしれないけど、とりあえず動作したので気にしない。どうせ使い捨てだし。

ピュア Ruby らしいし、便利とはいうても時間は多少かかるんでしょ…とかナメてたのだけど、実行してみたらひとつのファイルを処理するのになんか 4 秒くらいで終わってしまって、あまりのあっけなさに拍子抜けした。こんな速いの…もっと早く知っていれば時間を無駄にせずに済んだのに。涙

CSV さんすんませんでした!(土下座