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
実行結果
これどんないいことがあるの?
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にも存在しないので、「近しい文字」に置換するテーブルは業務ルールで足していけばいいんじゃないですかね。