Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

NKFで入力エンコーディングを省略して文字化けした話

Ruby 3.1.3、Rails 6.0.6で実装した際の話です。

irb(main):009:0> str = NKF.nkf('-x --ic=UTF-8 --oc=Shift_JIS', 'オオモリ')
=> "\xB5\xB5\xD3\xD8"
irb(main):010:0> str.force_encoding('UTF-8')
=> "\xB5\xB5\xD3\xD8"
irb(main):011:0> str = NKF.nkf('-x --oc=Shift_JIS', str)
=> "\x{8B54}\x{9A77}"
irb(main):012:0> NKF.nkf('-x --ic=Shift_JIS --oc=UTF-8', str)
=> "亀嗹"

NKFの--ic オプションは入力エンコーディング、--ocオプションは出力エンコーディング。
上記ではStringのencodingがUTF-8であるShift_JISな文字列を入力エンコーディングの指定なしで文字コードをShift_JISに変換した場合の例です。
入力エンコーディングの指定なしだと自動判定されるので例えばEUC-JPとして判定されると上記の文字化けが発生します。

なぜこんな実装をしたか

外部システムからShift_JISでPOSTリクエストを受けてその文字列を最終的にUTF-8として保存する実装をした際に、RailsはデフォルトだとパラメータをUTF-8として扱うため、String のencodingがUTF-8である文字列をShift_JISに変換する方法として--icを省略した自動変換に頼ろうとしたためです。

このバグはオオモリでは文字化けするが、オオモリタロウだと文字化けしなかったりするので気づかない場合があります。

irb(main):015:0> str = NKF.nkf('-x --ic=UTF-8 --oc=Shift_JIS', 'オオモリタロウ')
=> "\xB5\xB5\xD3\xD8\xC0\xDB\xB3"
irb(main):016:0> str.force_encoding('UTF-8')
=> "\xB5\xB5\xD3\xD8\xC0۳"
irb(main):017:0> str = NKF.nkf('-x --oc=Shift_JIS', str)
=> "\xB5\xB5\xD3\xD8\xC0\xDB\xB3"
irb(main):018:0> NKF.nkf('-x --ic=Shift_JIS --oc=UTF-8', str)
=> "オオモリタロウ"

param_encodingでエンコーディングを指定

param_encodingを使うと、指定したパラメータのエンコーディングを指定できるので自分で変換する必要はなくなります。
また、Rails 6.1以降だと、UTF-8として正当なバイト列でなければ、ActionController::BadRequest エラーになるので、UTF-8以外の文字列が渡ってくるのであれば、param_encodingを使う必要があります。

class TestController < ApplicationController
  param_encoding :create, :shift_jis_str, Encoding::Shift_JIS
  ...
  ...
end

まとめ

文字コードを変換する際は入力エンコーディングを自動変換に頼るのは避けた方が良さそうです。



CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。