Log目次

【Ruby】NKFを使用した文字コードの変換

作成日 2021-10-31更新日 2021-10-31

各種文字の変換

実行環境

ruby 2.6.4p104 (2019-08-28 revision 67798) [x86_64-linux]

# UTF-8からSHIFT-JISに変換 str = 'あ' => "あ" NKF.guess('あ') => #<Encoding:UTF-8> str2 = NKF.nkf('-s', str) => "\x{82A0}" NKF.guess(str2) => #<Encoding:Shift_JIS> # SHIFT-JISからUTF-8に変換 str3 = NKF.nkf('-w', str2) => "あ" NKF.guess(str3) => #<Encoding:UTF-8> # 「ひらがな」から「カタカナ」に変換 NKF.nkf('-wh2', 'あ') => "ア" # 「カタカナ」から「ひらがな」に変換 NKF.nkf('-wh1', 'ア') => "あ" # X0201片仮名(いわゆる半角片仮名)をX0208の片仮名(いわゆる全角片仮名)に変換する NKF.nkf('-wX', 'こんな感じ。ウボォ') => "こんな感じ。ウボォ"

注意点

意図しない文字化け

齊藤という文字をUTF-8やSHIFT-JISとして出力してみる

str = '齊藤' => "齊藤" NKF.nkf('-w', str) => "鮨願陸" # SHIFT-JISで出力しても同様に文字化けする(3文字出力されてる) NKF.nkf('-s', str) => "\x{E9BD}\x{8AE8}\x{97A4}"

調べてみると

手元のcalkiがUTF-8の「》」相当の文字(U+8BB)を含むエントリが文字化けするので、 nkf-utf8のソースを見てみた。 どうも自動判定の優先順位がEUC-JP,SJIS,JIS,UTF-8で固定されていて、 EUCの範囲内に収まる文字列はすべてEUC-JPとみなすことになっている。 で、UTF-8の「》」はEUC-JPの「損」と同じバイト列なのだ。1

なるほど。。。自動判定の優先順位がUTF-8よりもSHIFT-JISの方が高いのか。

NKF.guess('齊藤') => #<Encoding:Shift_JIS>

確かにSHIFT-JISとして判定されている。 もう一度バイト列を見てみる

'齊藤'.bytes.map { |b| "%02X" % b } => ["E9", "BD", "8A", "E8", "97", "A4"]

2バイトごとにSHIFT-JISのパターンに照らし合わせてみると

E9BD=>鮨

8AE8=>願

97A4=>陸

例えば、以下のように別の漢字をくっつけてみると

# ちゃんとUTF-8として認識されている NKF.guess('愛齊藤') => #<Encoding:UTF-8> # 文字化けしない NKF.nkf('-w', '愛齊藤') => "愛齊藤" '愛齊藤'.bytes.map { |b| "%02X" % b } => ["E6", "84", "9B", "E9", "BD", "8A", "E8", "97", "A4"]

左から順に2バイトずつ見ていくと「BD8A」に該当する文字はSHIFT-JISでは割り当てられていないので、UTF-8として認識されているのかも。

対処方法 入力した文字はutf-8であることを明示する

NKF.nkf('-wW', str) => "齊藤" # SHIFT-JISに変換するときも同様 NKF.nkf('-sW', str) => "\x{EA8E}\x{93A1}"

参考

Footnotes

  1. Matzにっき