Nodejs と iconv-lite と mac/windows と Shift JISと
NodeJSでShiftJISを扱うプログラムを作成したのですが、そのときに色々とハマりました。
知見を共有したいと思います。
macとwindowsのUnicode違い
ShiftJISを扱う方には有名な、波ダッシュ問題 をご存知の方は多いかと思います。
これは ShiftJIS (CP932) の 文字コード 0x8160 を Unicodeの文字に変換するときのテーブルの実装が異なっていることが原因による一連の不都合な事象を指しています。
ShiftJIS | 変換方法 | Unicode |
---|---|---|
0x8160(〜) | ShiftJIS | U+301C |
0x8160(〜) | CP932 | U+FF5E |
ShiftJISをUnicodeに変換するときの変換方法さえ気をつければなんとかなる…かと思いきや、Webサービス特有の問題がありました。
MacとWindowsで 伸ばし棒を変換した際に出てくる波線 〜
の文字が異なる文字だったのです。
機種 | 入力文字 | Unicode |
---|---|---|
mac | 〜 | U+301C |
Windows | ~ | U+FF5E |
(大体のフォントでは見た目は変わりません)
これにより、ユーザーに入力してもらった文字をShiftJISに変換する時に不具合が生じます。
例えばiconvを使って文字を変換することを仮定します。
echo "\u301c" | iconv -f UTF8 -t SHIFT_JIS
�`
echo "\uff5e" | iconv -f UTF8 -t SHIFT_JIS
iconv: (stdin):1:0: cannot convert
やはりU+FF5Eの場合は変換に失敗してしまいました。
これはShiftJISの文字空間としてU+301Cのみが0x8160にマッピングされ、U+FF5Eのマッピング先が存在しないことに起因します。
このことから、ユーザーに入力してもらう文字列を別の文字コードに変換する場合、気をつけなければならない文字が存在していることが分かりました。
処理プログラムでマッピングがバラバラ
では、Nodeで一般的に用いられる文字変換ライブラリ iconv-lite
を利用して文字の変換を行ってみます。
以下のプログラムを作成しました。標準入力の文字列を単純にShiftJISに変換するプログラムです。
const iconv = require("iconv-lite");
const dec = iconv.decodeStream("UTF-8");
const enc = iconv.encodeStream("ShiftJIS");
process.stdin.pipe(dec).pipe(enc).pipe(process.stdout);
これを利用して U+301C(〜)を変換します。
echo "\u301c" | node index.js
?
ナゼカヘンカンデキナイ…
調べたところこのようなissueがありました。
WHATWGの変換テーブル に則って変換しているから妥当だよ!
なぜWHATWGの変換テーブルは iconvとは異なる文字コードでマッピングされているのでしょう、謎は深まるばかりです… (調査に力尽きた)
ちなみに他のプログラムではこうなりました。
$ echo "\u301c \uff5e" | iconv -f UTF8 -t CP932 | hexdump -C
00000000 81 60 20 81 60 0a |.` .`.|
00000006
$ echo "\u301c \uff5e" | nkf -s | hexdump -C
00000000 81 60 20 81 60 0a |.` .`.|
00000006
$ echo "\u301c \uff5e" | nkf -s --fb-html | hexdump -C
00000000 81 60 20 26 23 36 35 33 37 34 3b 0a |.` ~.|
0000000c
大部分のプログラムでは、Unicode→ShiftJIS側の変換ではゆとりをもたせていて、複数候補がある場合はどちらも同じ文字 (0x8160) に変換するようです。
ライブラリ単位で文字コードの扱いが異なるので、ライブラリごとに何ができる、何が出来ないを適切に把握して最適なライブラリを選定しましょう。
まとめ
- Windowsとmacで波ダッシュを入力した時に出力される文字が違う文字である
- 波ダッシュをShiftJISに変換するときの規格も統一されているのかよくわからないのでライブラリごとに調査が必要
Discussion