いわゆる「文字化け」が発生した際、技術者は原因を調べようとするが、しばしば罠に陥り、原因を見逃したり、別の箇所でひっかかったりする。
主に Java を想定して、メモってみる。
文字コードに対する理解不足でハマる
文字コードを「文字をデータとして表すための決まりごと」くらいにしか理解していないと、原因発見はおぼつかない。
例えば、「文字集合 (文字セット)」と「エンコーディング (符号化方式)」の関係とか。Unicode は文字集合、UTF-16 や UTF-8 は Unicode 用のエンコーディングである。また、JIS X 0201 や JIS X 0208 は文字集合で、それに対応するエンコーディングとして Shift_JIS、EUC-JP、ISO-2022-JP などがある。
ふたごの「シフト JIS」にだまされてハマる
いわゆる「シフト JIS」には、2 種類ある。
- Shift_JIS
- JIS 規格に準拠した「シフト JIS」
- Windows-31J
- Windows 独自の「シフト JIS」。俗に言う機種依存文字 (丸付き数字とか、ギリシャ文字とか) が追加されている他、「〜」など一部の文字で扱いが変わることがある
例えば、メモ帳で作ったファイルに、"Shift_JIS" と文字コード指定するのは間違いで、 "Windows-31J" と書くのが正しい。
特に、Java については事情が複雑になっている。風間一洋氏による下記のページを読んでおきたい。
これがどう影響するかというと、JDK 1.3 でコンパイルしたソースを JDK 1.5 で動かしたら文字化け、といったことが起こるのだ。しかし、事情を考えずに「これ Java のバグ?」とか言っちゃダメですよ。
その言語での文字コードの扱いを知らずにハマる
Java や C# などでは、文字列は常に Unicode で扱われる。よくある勘違いを挙げてみる。
- 入力と出力が両方 Windows-31J なら文字化けは起きないはずだ
- ウソ。入力時に「Windows-31J→Unicode」、出力時に「Unicode→Windows-31J」という文字コード変換が行われるため、文字化けが発生する可能性はある。
- 内部形式が Unicode なので 1 文字が必ず char 1 個
- ウソ。Java や C# では、Unicode のコードそのままではなく、UTF-16 でエンコードしたものを内部形式としている。従って、1 文字が複数の char になることはある。
他の言語でいうと、D 言語は内部形式が UTF-8 なので、同じ Unicode 系だからといってそのまま Win32 の Unicode 版 API 関数に渡したりはできない、とか。
入力側が正しいと思い込んでハマる
以下のようなコードを書いたとする。
Writer w = new FileWriter(path); w.write("あ〜");
これで出力されたファイルが文字化けしていた時、出力の仕方が悪かったと考えるのは気が早い。
Java のソースコードは、コンパイルされる際に Unicode に変換される。この時、メモ帳で書いたのに文字コードとして Shift_JIS が指定された場合、コンパイルした時点で文字化けが発生する可能性がある。
コンソールに出力してみてハマる
ファイルからの入力時の文字化けが起きているのではないか、と思った場合、試しにコンソール (標準出力) に出して見てみる、という人は多いと思う。
String line = br.readLine(); System.out.println(line);
これで化けていたら入力側が文字化けの原因、と思うのは気が早い。
println() メソッドが標準出力へ出力する時に、文字コード変換が行われるからだ。
また、標準出力までは化けずに届いているのに、コンソールで正しく表示できていない、というパターンもありうる。特に、Telnet や SSH を使って他のマシン上で実行している場合には、文字コードの設定を確認した方がいい。
この罠を避けるためには、内部形式そのまま (Java なら UTF-16 あたり) でファイルに出力する (標準出力に出してリダイレクト、だと意味ないですよ) か、文字列内の各コードポイントを数字としてコンソールに出力してみる必要がある。
テキストエディタで開いてみてハマる
前項の最後で、ファイルに出力するといいと書いたが、これをテキストエディタで開く場合にも注意がいる。
- 「Unicode 対応」とされているエディタなら大丈夫だよね
- 甘い。「UTF-8/16 のファイルが扱える」というだけで、内部形式は Windows-31J というものも少なくないからだ。その場合、UTF-16 のファイルを開けても、「Unicode にあって Windows-31J にない文字」が捨てられてしまう。また、Unicode の一部文字は表示されない制御文字なので、内部形式が Unicode のエディタであっても、文字化けの確認に使う場合には注意が必要。
- 内部形式が Unicode のテキストエディタなら大丈夫だよね
- 甘い。上と逆のパターンで、Windows-31J のファイルを開く場合、エディタは Unicode に変換してから表示することになる。ここで文字化けが発生する可能性もあるわけで、やはり注意が必要。
- UTF-16 のファイルを、内部形式が Unicode のエディタで開けば大丈夫だよね
- 甘い。エディタはちゃんと読み込んでるんだけど、フォントが足りなくて表示できない、ということも起きうる。
結局、バイナリエディタでないとちゃんとした検証はできないのだ。