MacJDK6以降のstdoutSJISになる現象の根源


概要

Tさんに粘着して教えていただいたので、纏めておく。

Tさんすいませんでした。


参考

http://happygiraffe.net/blog/2009/09/24/java-platform-encoding/



参考の意訳

不運にも、コマンドラインでJavaを実行する際、UTF-8文字列(U+00A9 COPYRIGHT SIGN[?])を渡したものをprintしたら、二重にエンコードされてしまった。


とりあえず-Dfile.encoding=UTF-8で対処していたんだけど、

自動リスタートとかしてたら、反映されない。これは不味い。

で、私たちは問題をさらに調査した。


-Dつけてれば適応されるんだし、きっとLANG環境変数が欠如している事と関係があるだろうな、とあたりをつけた。



試しに、システムプロパティ一覧を表示するアプリケーションを作って、結果をgrepしてみた。

おなじみのfile.encodingがある。さらに内部に、sun.jnu.encodingっていうのが有るのを見つけた。


で、-Dfile.encodingをつけての挙動を試してみた。

sun.jnu.encoding が変わってない!


このプロパティに関するドキュメントは、探してみたけど知る限りどこにも無いみたいだ。

実際の挙動を知る為に、JDK6のソースコードを読むしか無い。


main関数はjava.cにあった。実際に使われているJavaMain()関数の中身をみると、

NewPlatformStringArray()関数が定義されていて、コマンドラインからの入力行ごとに実行されている。

その中では、 new String(byte[], encoding)が呼ばれている。

さらにその中で呼ばれている getPlatformEncoding() 関数で、


System.getProperty("sun.jnu.encoding")



が呼ばれている。

このプロパティはどこでセットされているんだ?

System.cだ。


その中の Java_java_lang_System_initProperties() 関数の中で、

PUTPROP(props, "sun.jnu.encoding", sprops->sun_jnu_encoding);

という行があり、


sprops-> sun_jnu_encodingは、  java_props_md.c ファイル中の、

GetJavaProperties() 関数でセットされている。


こいつは、いろいろな環境変数を含んでいる。ロケールをコントロールするものも含めて。


「入力されるいろんなもの」から、sun_jnu_encodingを取得する為に設定されているみたいだ。


ちぇっ。

判った事として、次のパラメータが、 sun_jnu_encoding の定義に使われる。


Command line 引数

メインクラス名

環境変数



で、このパラメータはoverrideできる。

$ LANG= java -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8


訳終わり



という訳で

初期エンコーディングの可能性は、この java_props_md.c らへんを見れば判る、と。

http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/536cbf2d9d0e/src/solaris/native/java/lang/java_props_md.c



GetJavaProperties メソッドの所には、

"Determine the language, country, variant, and encoding from the host"


って書いてある。これが元か。JDK5との比較しなきゃだけど、なぜこんな仕様になったんだろう。

インストーラの仕事サボった?


上からファイル追っていくと、sun.jnu.encodingの候補は、環境とかlocaleとかによってころころと変わっていく。

最終的に sun.jnu.encoding std_encoding の値を代入されるのだけれど、

std_encoding の値が下記のように変わる。



ISO8859-15 これはUS-ASCII


nl_langinfo(CODESET) こいつは langinfo.h / langinfo.c で定義されてる。 CODESETは環境によって異なる。

#ifdef __linux__


#define CODESET _NL_CTYPE_CODESET_NAME

#else


#ifdef ALT_CODESET_KEY

#define CODESET ALT_CODESET_KEY

#endif


#endif



ISO646-US 下記URLに色々載ってる。

http://www.asahi-net.or.jp/~yw3t-trns/code/iso646.html



ISO8859-1 Latin-1





さらに各環境の場合、下記がセットされる。


//en_UK だったら

ISO8859-1


//linuxだったら系

EUC-JP-LINUX 

eucJP-open

Big5_Solaris




で!

langinfo.c を探してみると、

すいませんよくわかりません。どこに有るんや。


下記適当に見つけてきた langinfo.c だと、localejpだったらSJIS返してる。

http://www.cl.cam.ac.uk/~mgk25/ucs/langinfo.c



続く。