発端
UTF-8 の CSV は Excel で文字化けするから。
メソッド
素晴らしいメソッドがありました。String クラスを拡張するメソッドです。
このメソッドの特徴は、「UTF-8 → Shift-JIS → UTF-8」という変換を行う点です。つまり、入力文字も出力文字も UTF-8 であるという点です。素的な設計だと思います。
もしShift-JISで文字列を吐き出したい場合は、この #sjisable
を施した文字列を「何も考えずに」Shift-JISに変換すればいいだけとなります*1。
以下は、上記の記事を発見する前の記事です(残しておきます)
メソッド(旧記載)
元々以下のページにあるものに手を加えただけです。
上記のメソッドをそのまま使っていたのですが、ある日エラーで出力が止まっていまして、エラーメッセージを見たところ以下のようなメッセージを吐いていました。
(省略) foobar.rb:331:in `encode': U+200B from UTF-8 to Windows-31J (Encoding::UndefinedConversionError) (省略)
U+200B
って何ぞ、と思い調べてみたところ、「ゼロスペース」「幅なしスペース」「zero-width space character」とのこと。これの処理は前述のメソッドには含まれていませんでした。
結論
したがって、当該メソッドを以下のように書き換えて事なきを得ました。[(UTF-8の文字),(SJISの文字)]という並びです。
def sjis_safe(str) [ ["301C", "FF5E"], # wave-dash ["2212", "FF0D"], # full-width minus ["00A2", "FFE0"], # cent as currency ["00A3", "FFE1"], # lb(pound) as currency ["00AC", "FFE2"], # not in boolean algebra ["2014", "2015"], # hyphen ["2016", "2225"], # double vertical lines ["200B", "" ], # zero-width space ].inject(str) do |s, (before, after)| s.gsub( before.to_i(16).chr('UTF-8'), after.to_i(16).chr('UTF-8'), ) end end
大結論
絵文字が入ってきたりするともうどうにもならないので、Shift JIS からは原則として決別するのが良いです。
*1:美しい