次のような、日本語のテキストを terminal に表示する簡単なプログラムを考えます。
public class Test { private static final String text = "日本語のテスト"; public static void main(String[] args) throws Exception { System.out.println(text); } } |
このように、Java のソースの中に、アスキー以外の文字を記述した場合は、どのような locale でも、ちゃんとコンパイルできるように、native2ascii コマンドで、Unicode のリテラルに変換しておくのが、推奨されています。しかし、開発環境の locale と、ソースコードの encoding が同じ場合は、Linux や Solaris での Sun の実装では、問題なく動作します。Mac OS X でも、下記のように、同様に動作するようにみえます。
$ echo $LANG en_US.UTF-8 $ javac Test.java $ java Test 日本語のテスト $ |
ところが、Servlet の中で、上記のように、日本語(non-ASCII characters)を UTF-8 でハードコードしたところ、文字化けが発生してしまいました。その Servlet は、JDBC で Derby データベースも使用しているので、最初は、Tomcat や Derby などの設定が悪いのかと思い、いろいろ調査したり、デバッグしてみましたが、どうも問
$ mv Test.java Test.txt $ native2ascii -encoding UTF-8 Test.txt > Test.java $ grep final Test.java private static final String text = "\u65e5\u672c\u8a9e\u306e\u30c6\u30b9\u30c8"; $ javac Test.java $ java Test ??????? $ |
今度は、7 文字となりましたが、System.out への出力で、? に化けてしまいました。これも、先ほどの javac と同様、system の default encoding を正しく、認識していないためのようです。もともと、System.out は、java.io.PrintStream (byte stream) ですが、Sun の実装では細工がしてあり、PrintWriter のように扱えますが、Mac OS X では、ISO-8859-1 とみなしているようです。そこで、明示的に、UTF-8 を encoding に指定して、Writer (character stream) を作成してみました。
$ cat Test.java public class Test { private static final String text = "\u65e5\u672c\u8a9e\u306e\u30c6\u30b9\u30c8"; public static void main(String[] args) throws Exception { PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out, "UTF-8")); out.println(text); out.flush(); } } $ javac Test.java $ java Test 日本語のテスト $ |
今度は、Mac OS X の Terminal.app 上でも、正しく表示されるようになりました。
Mac OS X 上で、Java のプログラムを開発するときは、default の encoding に頼らずに、アスキー以外の文字をハードコードした場合は native2ascii を使用するとか、正しく encoding を指定して Reader や Writer を作成するなど、注意が必要です。Terminal.app 上で、日本語(など)が正しく表示されるから、といって安心してはいけません。
Tags: programming