Ruby

Windows-31Jに変換する時の例外を防止するためのString拡張

More than 3 years have passed since last update.

String拡張

class String
  def sjisable
    str = self
    #変換テーブル上の文字を下の文字に置換する
    from_chr = "\u{301C 2212 00A2 00A3 00AC 2013 2014 2016 203E 00A0 00F8 203A}"
    to_chr   = "\u{FF5E FF0D FFE0 FFE1 FFE2 FF0D 2015 2225 FFE3 0020 03A6 3009}"
    str.tr!(from_chr, to_chr)
    #変換テーブルから漏れた不正文字は?に変換し、さらにUTF8に戻すことで今後例外を出さないようにする
    str = str.encode("Windows-31J","UTF-8",:invalid => :replace,:undef=>:replace).encode("UTF-8","Windows-31J")
  end
end

テスト

#変換テーブル上の文字が置換されていることを確認
from_chr = "\u{301C 2212 00A2 00A3 00AC 2013 2014 2016 203E 00A0 00F8 203A}"
p from_chr    
p from_chr.sjisable

#Shift_JISにもWindows-31Jにもない文字は諦めて?にする
p "ä".sjisable

# Windows拡張領域は保持されていることを確認
p "①②③髙島屋".sjisable

実行結果

スクリーンショット 2014-11-15 13.16.58.png

これどんないいことがあるの?

Rubyの内部コードはもはやUTF8。だけど、最終的にはエクセル向けにシフトJISのCSVを出力することもある。だけど、内部コードから出力時にエンコード変換をすると、対応する文字が見つからなくて、例外がでちゃう。例外はヤなので、多少は文字を置き換え(もしくは全く変換できないものは?に置換)してでも、処理を通したいということはある。

解説

Rubyでは、シフトJIS系のエンコード名には以下の種類がある。
* Shift_JIS
* Windows-31J
* SJIS(これはWindows-31Jのエイリアス)
* CP932(これはWindows-31Jのエイリアス)
SJISとCP932はエイリアスなので、実際はShift_JISとWindows-31Jの二つであると思っていい。
例えば、~という文字は、Shift_JISでもWindows31Jでも、コードとしては、「8160」が割り当てられえている。
* 8160をShift_JISとしてUNICODEに変換すると301C(波ダッシュ)
* 8160をWindows31JとしてUNICODEに変換するとFF5E(全角チルダ)
UNICODEは扱える文字が多いから、UNICODEで保持する間は被害が少ない。辛いのは、UNICODEからShift_JISやWindows-31Jに変換するとき。Windows-31Jは波ダッシュのことは知らないし、Shift_JISは全角チルダのことなんて知らない。シフトJIS系のテキストが求められる時ってWindowsが多いので、どっちに合わせるって言えば、Windows-31Jに合わせるケースが多い。

保守

from_chrとto_chrの文字を増やしていけば、変換する文字は増やせる。trメソッドは、文字単位で置換してくれるので便利ですね。~とかはありがちなんだけど、øとかは、Shift_JISにもWinndows-31Jにも存在しないので、「近しい文字」に置換するテーブルは業務ルールで足していけばいいんじゃないですかね。