CSV ファイルを一行ずつ処理するプログラムを実行したら一行目のデータでエラーになったが、ログをみてもファイルをエディタや cat(1) でみてもおかしなところはない、ということがあって、 BOM かな?と思ったけど BOM つきかどうか確認するのに手間取ったので、次回(何年後だろう)のためにメモ。
BOM がついてるか確認する
more(1) か less(1) で開いてみてファイル先頭に <U+FEFF>
があったらビンゴ。 file(1) でも教えてもらえる。 Emacs でファイルを開いて BOM を確認する・表示させるのはやり方を見つけられなかった。
hexdump(1) でファイル先頭に? ef bb bf
あるか見るのでももちろんよいが、普段からバイナリファイルのダンプを見慣れていないと見てもとっさに BOM つきであることがわからないので、一目見て明らかにおかしいとわかる more(1) less(1) のほうがわかりやすいと思う。
BOM の削除方法
https://github.com/kyanny/playground/blob/gh-pages/bom/test_nobom.rb
ふつうに文字列置換で \uFEFF を削除すればよい。
text.sub!(/\A\uFEFF/, "")
BOM つき UTF-8 ファイルの作り方
https://github.com/kyanny/playground/blob/gh-pages/bom/bom.rb
これもふつうに \uFEFF をファイルの先頭に書き込めば良い。
"\uFEFF" + "Hello,World!"
ちなみに実際起こったエラーはこれにやや近く、 CSV ファイルをパースして一列目にあるコード値でデータベースから find_by_code! したら RecordNotFound 的な例外が出てしまった、というものだった。 backtrace などには BOM は表示されなかったので、一見するとちゃんとデータベース上に存在して rails console からはレコードを引ける値なのにコケている、という状況だった。実際にはコードが 123ABC だとしたら \uFEFF123ABC という値で find していたので見つからない、ということだった。