はじめに
こんにちは!
リアルタイム文字コード解析ツールの作者のなたでです(宣伝)

今回は前から気になっていたShift_JISの文字コードの変換に使われるiconvとnkfの動作について調査しました。
以前、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はこの仕様なのでしょうか。このあたりは、以下の記事が参考になります。
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の多くの実装では8150の―が2015の―へ変換する動作だったんですが、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 をデフォルトで用いる。
詳細は以下の仕様を確認すると良いです。
さいごに
最後まで読んでいただきありがとうございます。
Shift_JISではなくWindows-31Jやcp932を選んでおけば文字コードのエンコードの問題は解決すると思っていたかもしれませんが、使用するライブラリによってこんなにも差があるんですね。
iconvの方が有名ですし設計の観点から推奨している記事も多いですが、Windows-31Jとの互換性の観点を考えるとnkfの方が優秀であるため、適材適所かなとは考えております。



コメント