2012年9月12日水曜日

Javaで特定の文字が文字化けする

Javaで、機種依存文字では無い特定の文字が文字化けをするという問題があります。「〜‖—−¢£¬」の7文字がそれにあたります。「�」や「�」等の機種依存文字が化ける問題はあたりまえなので取り上げません。

問題が発生するのは、以下の2つの条件を満たす場合です。
  • SJIS系の文字コードを使用している
  • 入力時と出力時で異なる文字コードを使用している
逆に言えば入力時と出力時で文字コードを同一のものにすれば何も問題はありません

Webシステムなどでは画面から入力を受付け、それをDBに格納し検索時に表示するなどの使われ方をしますので、入力時と出力時であえて異なる文字コードを指定するケースは少ないはずです。ですから、実質的に問題が生じるのは、実装者が「何も指定していない」「何を指定しているかわからない」「調べ方もわからない」という場合が多いのではないかと思います。異なる文字コードを使用する複数のシステムを結合している場合にもこの問題が発生する可能性があります。

「〜‖—−¢£¬」の7文字はJIS X 0208で定められた範囲の文字であり、JISでもSJISでもMS932でもCP943でもEUC-JPでも共通して使える普通の文字です。機種依存文字ではありません。にもかかわらず文字化けします。

Java内部ではUnicodeを使用しています。そのため、どんな文字も一旦Unicodeデータとして格納されます。このとき、Unicodeのどの文字として格納するかのマッピング情報が必要になります。このマッピング情報が文字コードごとに少しずつ異なることが原因です。


具体的にセントマーク「¢」について考えます。「¢」はJIS X 0208で定められた文字です。つまり、日本語としての文字集合の範囲です。全角の「A」「0」「¥」などと近い扱いといえるでしょう。

Unicodeでは「¢」に相当するものは「U+00A2」と「U+FFE0」の2箇所で定められています。「U+00A2」があるエリアは、半角英数字記号のASCII文字が定められている領域の近くで、「U+FFE0」があるエリアは、全角の英数字記号や半角カナが定められているエリアです。個人的にはSJISとしての「¢」を格納するのは「U+FFE0」のほうが適切であるとは思いますが、とにかく、1つの文字を格納する候補がUnicodeでは複数あった、という点が問題の源泉です。そしてこのマッピングをベンダがそれぞれ独自に定めたため、文字コードによって同じ文字をUnicode上のどこに格納するかが異なる文字が発生してしまいました。

SJISでは「¢」をUnicodeの「U+00A2」に割り当てており、MS932では「¢」をUnicodeの「U+FFE0」に割り当てています。逆に、SJISではUnicodeの「U+FFE0」を変換できない文字としています。このため、MS932で入力した「¢」はSJISとして出力すると「?」に文字化けするのです。ところがMS932ではUnicodeの「U+00A2」を「¢」に変換できます。これはMS932が互換性を考慮し柔軟な設計にした結果でしょう。

このような仕組みで「〜‖—−¢£¬」の7文字は、条件によって文字化けします。といっても入力時と出力時に同じ文字コードを指定すればいいだけの話なので、それさえわかれば実質的には対策は容易でしょう。

¢ £ ¬
SJIS U+301C U+2016 U+2014 U+2212 U+00A2 U+00A3 U+00AC
CP943 U+301C U+2016 U+2014 U+2212 U+FFE0 U+FFE1 U+FFE2
MS932 U+FF5E U+2225 U+2015 U+FF0D U+FFE0 U+FFE1 U+FFE2
上記はそれぞれの文字を各文字コードで指定したときにUnicodeでどこに格納されるかの対応表です。


¢ £ ¬
SJIS→CP943 × × ×
CP943→SJIS × × ×
SJIS→MS932 × × × × ¢ £ ¬
MS932→SJIS × × × × × × ×
CP943→MS932 × × × × ¢ £ ¬
MS932→CP943 × × × × ¢ £ ¬
実際にJavaで実行した結果がこれです。「×」は文字化けした文字。基本的には、文字コードごとのマッピング先が異なれば、文字化けすると考えていいでしょう。例外的にMS932は「U+00A2」「U+00A3」「U+00AC」も正しく戻せるようにしているようです。

0 件のコメント:

コメントを投稿