2004年10月 8日

Ruby: 日本語の文字列を UTF-8 に変換する

日本語の文字列 (euc-jp, shift_jis, iso-2022-jp, utf-8 のいずれかわからない) を UTF-8 に変換しようと思った。
最初は samidareに含まれる Mconv.guess_charset を試したが、巨大なテキストを扱うとかなり遅かったので、ここここここを参考にして、次のようなコードを書いた。

 
class Iconv
  Preference = ["iso-2022-jp", 'euc-jp', 'utf-8',
    'shift_jis', 'windows-31j']
  def self.tou8 (str)
    return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # us-ascii
    Preference.each {|name|
      begin
        return Iconv.conv("UTF-8", name, str)
      rescue Iconv::IllegalSequence
      end
    }
    raise 'unable to convert to UTF-8'
  end
end

Iconv につっこんでみて、エラーが起きたら次の候補を試す、という単純な仕組みである。このとき、試す順序は誤変換を防ぐために重要で、akr氏が検討を行った Mconv の順序を見習った。ただし、 glibc の iconv は厳格で、 shift_jis では丸数字などを許さないので windows-31j も加えておいた。

上のコードでは文字列のチェックを完全に iconv に頼っているので、チェックが甘い iconv だと困るかもしれない。また、Mconv.guess_charset ではちょっとくらいの間違い (euc-jp で丸数字を使っているとか) は許容するが、上のコードでは glibc の iconv を使うと厳格にはじかれてしまう。

追記: Preference の先頭から us-ascii を外して、正規表現マッチで判別するようにしました。手元の iconv では iso-2022-jp の文字列を Iconv.conv("UTF-8", "us-ascii", str) でエラーなしに無変換で通すことに気づいたためです。