iconvとnkfのShift_JIS系列の変換動作を調査してみた

アルゴリズム
この記事は約9分で読めます。
スポンサーリンク

はじめに

こんにちは!

リアルタイム文字コード解析ツールの作者のなたでです(宣伝)

リアルタイム文字コード変換/解析ツール
手軽に文字の調査及び変換を行える専用サイトです。リアルタイムにコードが表示されます。

今回は前から気になっていたShift_JISの文字コードの変換に使われるiconvnkfの動作について調査しました。

以前、cp932の動作仕様が気になり調べたことがありました。その際はWindows環境では一般的なMultiByteToWideChar / WideCharToMultiByteを使用して調べたのですが、他のOSで使用されているようなiconvとnkfの動作も気になり、今回調べることとしました。

結果をブログにまとめている最中に、Nateさんという方がnkfとiconvの差異について調べていましたので、参考に紹介します!→ Nate 2015年5月10日 nkfとiconvの差異

方法

今回私が調べたバージョンは現在最新版の以下となります。

調べ方は、前回調べたような文字コードの全範囲をUnicodeへ変換及び逆変換を行うという方法を取ります。

基本はiconvとnkfとの比較し、Windows-31Jの変換調査をする際は、Windows公式動作であるMultiByteToWideChar / WideCharToMultiByteとの検証を行います。

出力結果はGitHubで公開しております。

ここから先はこの結果を元にした私目線のレポート記事となります。

Shift_JISの変換の比較

結果

iconvのオプションはShift_JISを指定。nkfも同じくShift_JIS(-sオプションと同等)を指定して調査。薄い赤色の部分は注意が必要な部分。

Shift_JIS iconvの解釈 nkfの解釈
005C \ 00A5 ¥ 005C \
007E ~ 203E 007E ~
1バイトカタカナ 半角カタカナ 全角カタカナ ⚠️
8150 FFE3 203E
815C 2015 2014
818F FFE5 00A5 ¥
NEC特殊文字 , Unicodeへは変換可、逆は不可 変換可能​
MacJapanese , 変換不可 ​変換可能​
IBM拡張文字 Unicodeへは変換可、逆は不可 ​変換可能​

⚠️ nkfでは、-xの指定を追加することで半角カナから全角カナへの変換を行わないといった指定が可能です。デフォルトでは全角カタカナとする動作を行います。

感想

全体を通して、iconvの方が厳密です。今回、Windows31-Jは指定していませんが、拡張文字のUnicodeへの一方通の変換は可能のようです。nkfはできる限り変換しようという気持ちが表れているようです。MacJapaneseという私も知らなかった文字コードに対応していました。

1バイト文字については、Shift_JISの仕様の解釈違いなどで差がありました。こちらもiconvの方が厳密な動きになっています。半角カタカナについてはiconvの仕様、バックスラッシュとチルダはnkfの仕様というような、1バイト文字はそのまま通すといったcp932の仕様だと助かりますが、そうではないので注意が必要です。

全角の円マークは、iconvだと全角の円マークに変換されますが、nkfだと半角の円マークに変換されます。ここはiconvの動作が好みです。

iconv、nkfともに、WindowsのShift_JIS(Windows-31J)だと思ってShift_JISをエンコードの設定に使用すると、1バイト文字の動作で問題が起こる危険性があります。

Windows31-Jの変換の比較

結果

iconvのオプションはCP932を指定、nkfも同じくCP932を指定して調査。今回は正解の本家動作があるため、解釈というよりは動作としました。また違いが合った箇所の他に、Shift_JISで違いがあった部分も記載します。薄い赤色の部分は注意が必要な部分。

CP932 本家動作 iconvの動作 nkfの動作
005C \ 005C \ 005C \ 005C \
007E ~ 007E ~ 007E ~ 007E ~
1バイトカタカナ  半角カタカナ 半角カタカナ 半角カタカナ
8150 FFE3 FFE3 FFE3
815C 2015 2015 2015
8160 FF5E 301C ⚠️ FF5E
8161 2225 2016 ⚠️ 2225
817C FF0D 2212 ⚠️ FF0D
8191 FFE0 00A2 ¢ ⚠️ FFE0
8192 FFE1 00A3 ⚠️ FFE1
81CA FFE2 00AC ¬ ⚠️ FFE2
MacJapanese 変換不可 変換不可 変換可能

⚠️iconvでUnicodeからCP932へ戻すことが出来ない一方通行の変換となります。

Windows-31Jでは、NEC特殊文字、NEC選定IBM拡張文字について、同一文字にも拘らず複数のコードで登録されています(コード重複文字と呼ばれる)。これらについてはUnicodeからCP932へ戻す際に、どちらのコードへ統一するかについては動作が違う部分がありました。

具体的な例で説明しますとUnicodeのコードポイント2160は、iconvでUnicodeからWindows31-Jへ変換するとFA4Aに変えられます。nkfでは8754としていますので、互換性を考えるとnkfの方が高いです。

感想

nkfは、Windows-31Jとの互換性が非常に高いことが分かりました。MacJapaneseの範囲の文字が含まれていると、変換してしまいますが、通常はこの範囲の文字は入っていないため、問題はないと考えられます。

一方でiconvは、一部厳しい箇所がありました。波線のが、全角のに変換されず、301Cに変換されます。本家の動作が違うものの一応見た目的には問題は起きないのですが、元のWindows-31Jに文字コードを戻す操作をすると、変換できず戻せない問題が発生します。

日本語の文章では、は「~から」みたいな意味を示し一般的であることから、iconvを使用すると不具合につながる可能性があります。

なぜOSSとして有名なiconvはこの仕様なのでしょうか。このあたりは、以下の記事が参考になります。

波ダッシュ、全角チルダ問題まとめ - Qiita
そもそも、波ダッシュと、全角チルダって? 波ダッシュ 〜 0x8160(Shift_JIS) 0x301C(UTF-8) WAVE DASH(ユニコードポイント : U+301C) 日本語の文字 範囲を表す : 大阪 〜 東京 など 省略記...

CP932という仕様がWindowsとの互換性を合わせるために使うという目的を考えると、iconvのWindows互換仕様をより高めた方が利用者は助かると思いますが(だってそういう文字コードなので)、変更がないという点を見ると、何か事情があるのかもしれません。

少なくとも、iconvとnkfの両方がある環境でかつ、Windowsとの互換性を高めたい目的で文字コードを変換する場合は、現時点でiconvよりもnkfを使用した方が問題が起こりません。

Shift_JIS-2004の変換の比較

結果

iconvのオプションはSHIFT_JISX0213を指定、nkfではShift_JIS-2004(文字集合に JIS X 0213:2004を用いた Shift_JIS)を指定して調査。

Shift_JIS-2004 iconvの解釈 nkfの解釈
005C \ 00A5 ¥ 005C \
007E ~ 203E 007E ~
1バイトカタカナ 半角カタカナ 全角カタカナ ⚠️
8150 FFE3 203E
815C 2014 2014
818F FFE5 00A5 ¥

⚠️ nkfでは、-xの指定を追加することで半角カナから全角カナへの変換を行わないといった指定が可能です。デフォルトでは全角カタカナとする動作を行います。

不可解な動作

Shift_JIS-2004は知名度が低く誰も困らないと思いますが、不可解な動作を発見したのでメモとして残します。

  • iconvでは82A9~82B1の「かきくけこ」について、Shift_JIS-2004からコードポイントへ変換できない。
  • iconvでは「セツト」のコードポイントからShift_JIS-2004へ変換できない。
  • iconvでは「æɔʌəɚ」のコードポイントからShift_JIS-2004へ変換できない。
  • iconvでは「˥˩」のコードポイントからShift_JIS-2004へ変換できない。
  • iconv、nkfともに82F5~82F9の空白の部分に対して、Unicodeへ変換すると「かきくけこ」へ変換される。

感想

Shift_JIS-2004は、他のコードよりも仕様が明確になっているため、iconv、nkfともに差分は少なかったです。ただ、ASCIIコード範囲はiconvの仕様による差分があったり、不可解な動作をする部分が見られました。

またShift_JIS-2004の自体問題なのですが、Shift_JISやWindows-31Jの多くの実装では81502015へ変換する動作だったんですが、Shift_JIS-2004では、2014へ変換するという明確な規則になっているようです。過去のコードとの互換性を考えると問題に見えます。

どちらにせよ、もし変換を考えるのであれば、iconvの「かきくけこ」が変換できない動作が致命的のため、iconvよりnkfの方が安定して変換できるかと思われます。​

nkfの注意点

ドキュメントをしっかり読むと、nkfの自動エンコード判別は半角カタカナを認識しない場合がありそうな記述があることが分かる。そのため入力の文字コードが分かっているのであれば、指定した方が安全かと思われる。自動判別するので指定しなくていいと書いてある記事もあるので注意が必要

実は、入力のコード系の判定は、Shift_JIS と EUC との自動判定であり、最初に特定できる文字が来た時点で確定してしまう。そして、特定不能の間は保留バッファにためておかれ、確定後に処理される。 このため、7 ビット JIS は常に認識される。Shift_JIS、EUC 混合もほとんどの場合は問題がない。 ただし、Shift_JIS のいわゆる半角カナ (JIS X 0201 片仮名)と EUC 漢字が識別できない。 したがって、デフォルトでは Shift_JIS のいわゆる半角カナはないと仮定している。(つまり、Shift_JIS か EUC か迷った時は EUC とみなす)

さらにいうとShift_JISと判定したとしても半角カナから全角カナへ変換してしまうため、やはり文字コードは必ず指定した方がよい(-xを指定すれば避けられますが)。

-x通常おこなわれる、いわゆる半角カナ (JIS X 0201 片仮名) からいわゆる全角カナ (JIS X 0208 片仮名)への変換を行わず、半角カナを保存する。 入力は、Shift_JIS の 1byte カナ、SO/SI、ESC-(-I, SSOを受け付ける。 出力は、日本語 EUC 中では SSO、JIS コード中では ESC-(-I をデフォルトで用いる。

詳細は以下の仕様を確認すると良いです。

nkf/nkf.doc at master · nurse/nkf
Network Kanji Filter. Contribute to nurse/nkf development by creating an account on GitHub.

さいごに

最後まで読んでいただきありがとうございます。

Shift_JISではなくWindows-31Jcp932を選んでおけば文字コードのエンコードの問題は解決すると思っていたかもしれませんが、使用するライブラリによってこんなにも差があるんですね。

iconvの方が有名ですし設計の観点から推奨している記事も多いですが、Windows-31Jとの互換性の観点を考えるとnkfの方が優秀であるため、適材適所かなとは考えております。

コメント