Ruby 標準添付ライブラリの CSV で Windows-31J (Shift_JIS でも良いけど) な CSV を作っていたのだけれども、特定の文字が含まれていると Encoding::UndefinedConversionError
になってしまう。
irb(main):011:0> CSV.open(File.join('/var/tmp/hoge.csv'), 'w', encoding: 'Windows-31J') do |csv|
irb(main):012:1* csv << %w( d é f )
irb(main):013:1> end
Encoding::UndefinedConversionError: U+00E9 from UTF-8 to Windows-31J
最初は気楽に undef とか replace とか指定すれば良いんでしょ、とか思っていたんだけど。
irb(main):014:0> CSV.open(File.join('/var/tmp/hoge.csv'), 'w', encoding: 'Windows-31J', undef: :replace, replace: '*') do |csv|
irb(main):015:1* csv << %w( d é f )
irb(main):016:1> end
ArgumentError: Unknown options: undef, replace.
CSV.open
に undef も replace も渡せない。
(話の流れで省略してるけど invalid も同じように渡せない)
実際に例外出しているのは CSV.new
で、 CSV.new
には決まったオプションしか渡せない。
書き出しの手間を考えると CSV ライブラリは使いたい、でも undef, replace 使いたい。
でも CSV.new には undef とか渡せない、でも文字コード変換して CSV 書き出したい。
とかぐるぐるしてたんだけれども。
実際に CSV.open
が何をやっているかを参考にしつつ。
File.open
して、それを CSV.new
に渡すことにした。
irb(main):017:0> File.open(File.join('/var/tmp/hoge.csv'), 'w', encoding: 'Windows-31J', undef: :replace, replace: '*') do |file|
irb(main):018:1* csv = CSV.new(file, encoding: 'Windows-31J')
irb(main):019:1> begin
irb(main):020:2* csv << %w( d é f )
irb(main):021:2> ensure
irb(main):022:2* csv.close
irb(main):023:2> end
irb(main):024:1> end
=> <#CSV io_type:File io_path:"/var/tmp/hoge.csv" encoding:Windows-31J lineno:1 col_sep:"," row_sep:"\n" quote_char:"\"">
これで、例外にならない。
$ cat /var/tmp/hoge.csv
d,*,f
指定の通りに replace されてる。