動かざることバグの如し

近づきたいよ 君の理想に

RubyでUTF-8をShiftJISに変換するならnkfを使うべき

WindowsとかいうゴミOSを考慮するせいで、もうすぐ東京オリンピック開催の2020年に近づいたというのにShiftJISで出力したいケースがある。

.encode()使えばいいんでしょ?

RubyはもともとUTF-8なのでUTF-8→ShiftJISの変換になりstr.encode(Encoding::Windows_31J)でいける。訳がなかった

irb(main):001:0> str = "〜鎌倉幕府〜"
=> "〜鎌倉幕府〜"
irb(main):003:0> str2 = str.encode(Encoding::Windows_31J, undef: :replace).encode('UTF-8')
=> "?鎌倉幕府?"

は????なんで文字化けしてるんだ

というのもUTF-8にあたる文字がShiftJIS(CP932)にないため。

そこで前もって該当する文字列たちを変換する処理をしていたのだが、(このあたりはString#encodeが変換できそうで変換できない文字 - ネットの海の片隅で参考)

def encode_to_jis(str = '')
  mappings = {
    "\u{00A2}" => "\u{FFE0}",
    "\u{00A3}" => "\u{FFE1}",
    "\u{00AC}" => "\u{FFE2}",
    "\u{2016}" => "\u{2225}",
    "\u{2212}" => "\u{FF0D}",
    "\u{301C}" => "\u{FF5E}"
  }
  mappings.each{|before, after| str.gsub!(before, after) }
  str.encode(Encoding::Windows_31J, undef: :replace)
end

が、時々例外が発生する。何故かはよくわからん(検証面倒

解決策

結局nkf使ったほうが速かったって話

require "nkf"
NKF.nkf("--ic=UTF-8 --oc=CP932", str)

icが入力の文字コード、ocが出力の文字コード。置き換えがないぶんこっちのほうが速かったりする。

置き換えできなかった文字は下駄記号に変換したい

下駄記号ってのはこれ --fb-subcharオプションをしたいすればおk

NKF.nkf("--ic=UTF-8 --oc=CP932 --fb-subchar=0x3013", str)

結論

ShiftJIS(CP932込)は○ね