Ruby1.9でEUC-JPの機種依存文字を処理する
※本記事は自鯖Blogの転載です
※元記事は2012/10/15に書かれました
http://blog.parrot-studio.com/2012/10/ruby19-euc-cp51932/
古いシステムがEUC-JPで動いていて、
そこからのリクエストをRubyのSinatra(あるいはPadrino)で受ける・・・
・・・そんなこと、よくありますよね(´・ω・)? *1
文字コードの変換は面倒なもので、ここがネックになることが多いのですが、
携帯絵文字まで対応しているRuby1.9系ならきっと大丈夫
・・・そんな風に考えていた時期が私にもありましたΣ(・ω・ノ)ノ
[1] pry(main)> RUBY_VERSION => "1.9.3" [2] pry(main)> 'あ'.encoding => #<Encoding:UTF-8> [3] pry(main)> 'あ'.encode('EUC-JP') => "\x{A4A2}" [4] pry(main)> 'あ'.encode('EUC-JP').encode('UTF-8') => "あ"
ここまではいいんですよ
問題は「機種依存文字」の場合でして
[5] pry(main)> '①'.encoding => #<Encoding:UTF-8> [6] pry(main)> '①'.encode('EUC-JP') Encoding::UndefinedConversionError: U+2460 from UTF-8 to EUC-JP from (pry):6:in `encode'
この文字にマッピングがないのは「文字コードの仕様」です
なので、これはある意味で正常な動作です
バイナリ表現をしてもダメです(´・ω・`)
("\xAD\xA1"は「丸に1」のEUC-JPバイナリコード)
[7] pry(main)> "\xAD\xA1".encode('EUC-JP') Encoding::InvalidByteSequenceError: "\xAD" on UTF-8 from (pry):7:in `encode'
そもそもそんなコードはUTF-8の範囲にないといわれますが、
ブラウザから送られてくる文字列にこれが含まれているわけです
さて、どうしたものか・・・
ここで引っかかっていろいろ調べた結果、
「EUC-JP」と呼ばれるものにもいろいろ種類があると判明
「Shift_JIS」と呼ばれるものが、実際には「CP932」だったりするように、
「EUC-JP」と呼ばれるものの「正式な名前」が他にあるはず、ということです
そこで、Rubyのリファレンスとにらめっこしたところ・・・
http://doc.ruby-lang.org/ja/1.9.3/class/Encoding.html
・・・「CP51932」というのが実に怪しいわけでございます
早速encodeしようとすると・・・
[8] pry(main)> "\xAD\xA1".encode(Encoding::CP51932) Encoding::InvalidByteSequenceError: "\xAD" on UTF-8 from (pry):8:in `encode'
・・・ダメですΣ(゚Д゚)ガーン
「"\xAD\xA1"」という文字列にUTF-8のEncodingが付加されているので、
force_encodingで解釈し直さないとダメです
ちなみに、Sinatra(Rackの仕様かも?)が
EUC-JPのフォームからリクエストを受け取る場合、
文字列にUTF-8のEncodingを付加するため、同じ現象が起きます
ということで・・・
[9] pry(main)> "\xAD\xA1".force_encoding(Encoding::CP51932) => "\x{ADA1}" [10] pry(main)> "\xAD\xA1".force_encoding(Encoding::CP51932).encode('UTF-8') => "①" [11] pry(main)> '①'.encode(Encoding::CP51932).encode('UTF-8') => "①"
・・・レガシーなシステムからのリクエストだって大丈夫
そう、Ruby1.9ならね(`・ω・´)
なお、この仕組みを実装したシステムで動作確認したところ、
MacのChrome/Opera、Windows7のIE9/Chromeで正常に動いたので、
おそらく大丈夫だとは思いますが、必ずテストはしましょうね *2