🐕

Rubyで波ダッシュ(〜)の文字コード変換

2024/01/29に公開

概要

Rubyで文字コードをUTF-8からSJISに変換したCSVを扱っている際に、波ダッシュ(〜)を含むデータがCSVに存在している場合に、期待している動作にならないことがありました。
調査をすると波ダッシュ・全角チルダ問題なるものがあると知りました。
波ダッシュ・全角チルダ問題に関しては、参考文献に記載したリンク先にとても詳しく記載されているので、そちらを参照してください。
この記事では

  • そもそもSJISってなんだ?
  • Rubyの各メソッドでの波ダッシュ・全角チルダの文字コード変換

という2点を記載しようと思います。
※ 以下では基本的にRubyに関連する話をするので、他の言語では事情が異なる場合があります。

そもそもSJISとは

今までSJISのことを「Shift_JISの略語」というフワッとした理解をしていたのですが、どうやら怪しそうです。
以下に記載したのですが、SJISがShift_JISを指しているものと、Windows-31JというShift_JISを拡張した文字コードを指しているものがあります。

EncodingクラスでのSJIS

EncodingクラスでのSJISはWindows-31Jを指しているようです。

[1] pry(main)> Encoding::SJIS
=> #<Encoding:Windows-31J>

https://docs.ruby-lang.org/ja/latest/class/Encoding.html#C_-C-P932

NKFモジュールでのSJIS

NKFモジュールでのSJISはShift_JISを指しているようです。

[1] pry(main)> NKF::SJIS
=> #<Encoding:Shift_JIS>

https://docs.ruby-lang.org/ja/latest/class/NKF.html#C_-S-J-I-S

波ダッシュ・全角チルダの文字コード変換

以下に波ダッシュ・全角チルダの

  • Stringクラスのencodeメソッド
  • NKFモジュールのnkfメソッド

での文字コード変換の結果をコードで記載しました。
個人的に曲者だなと感じたのは

  • NKFモジュールのnkfメソッドで波ダッシュをUTF-8 → Windows-31J変換した際に空白文字になる
  • 波ダッシュをWindows-31J → UTF-8変換した際に全角チルダになる

という2点でした。

UTF-8 → Shift_JIS, Windows-31J

Stringクラスのencodeメソッドでの変換

[1] pry(main)> "〜".encode("Shift_JIS") # 波ダッシュ
=> "\x{8160}" # 波ダッシュ
[2] pry(main)> "~".encode("Shift_JIS") # 全角チルダ
Encoding::UndefinedConversionError: U+FF5E from UTF-8 to Shift_JIS
from (pry):2:in `encode'
[3] pry(main)> "〜".encode("Windows-31J") # 波ダッシュ
Encoding::UndefinedConversionError: U+301C from UTF-8 to Windows-31J
from (pry):3:in `encode'
[4] pry(main)> "~".encode("Windows-31J") # 全角チルダ
=> "\x{8160}" # 波ダッシュ

NKFモジュールのnkfメソッドでの変換

[1] pry(main)> NKF.nkf("--oc=Shift_JIS", "〜") # 波ダッシュ
=> "\x{8160}" # 波ダッシュ
[2] pry(main)> NKF.nkf("--oc=Shift_JIS", "~") # 全角チルダ
=> "\x{8160}" # 波ダッシュ
[3] pry(main)> NKF.nkf("--oc=Windows-31J", "〜") # 波ダッシュ
=> ""
[4] pry(main)> NKF.nkf("--oc=Windows-31J", "~") # 全角チルダ
=> "\x{8160}" # 波ダッシュ

Shift_JIS, Windows-31J → UTF-8

Stringクラスのencodeメソッドでの変換

[1] pry(main)> "\x81\x60".encode("UTF-8", "Shift_JIS").unpack1('U*').to_s(16)
=> "301c" # 波ダッシュ
[2] pry(main)> "\x81\x60".encode("UTF-8", "Windows-31J").unpack1('U*').to_s(16)
=> "ff5e" # 全角チルダ

NKFモジュールのnkfメソッドでの変換

[1] pry(main)> NKF.nkf("--ic=Shift_JIS --oc=UTF-8", "\x81\x60").unpack1('U*').to_s(16)
=> "301c" # 波ダッシュ
[2] pry(main)> NKF.nkf("--ic=Windows-31J --oc=UTF-8", "\x81\x60").unpack1('U*').to_s(16)
=> "ff5e" # 全角チルダ

まとめ

  • SJISがShift_JISのことを指すか、Windows-31Jのことを指すか確認推奨
  • 波ダッシュ・全角チルダの文字コード変換にShift_JISやWindows-31Jが関係する際は注意

参考文献

https://secret-garden.hatenablog.com/entry/2022/05/11/212649
https://www.tohoho-web.com/ex/dash-tilde.html
https://weblabo.oscasierra.net/shift_jis-windows31j/

Discussion