Skip to content

Instantly share code, notes, and snippets.

@seraphy
Last active March 21, 2022 06:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seraphy/ebf6a2c084a66d8478b9502535ed1b5a to your computer and use it in GitHub Desktop.
Save seraphy/ebf6a2c084a66d8478b9502535ed1b5a to your computer and use it in GitHub Desktop.
Javaのデフォルトの文字コードの決められ方の調査

(結論) Javaのデフォルトの文字コードは決まっていない。

Javaのデフォルトの文字コードは決まっていない。実行時の環境により都度かわる。それが結論。

JVMバージョンによって文字コードが変わるわけではない

以下のようなコードをWindows7/10のEclipse(Pleiades 2018.12)で実行した場合、

String[] names = {
		"java.version",
		"java.vm.version",
		"java.vendor.url",
		"file.encoding",
		"sun.jnu.encoding"
};
for (String name : names) {
	System.out.println(name + "=" + System.getProperty(name));
}
System.out.println("default encoding=" + Charset.defaultCharset().name());

結果は、

java.version=1.8.0_202
java.vm.version=25.202-b08
java.vendor.url=https://adoptopenjdk.net/
file.encoding=UTF-8
sun.jnu.encoding=MS932
default encoding=UTF-8

こんな感じになる。

他のJava実行環境で試しても

  • Oracle JDK6
  • Oracle JDK8
  • AdoptOpenJDK 8
  • AdoptOpenJDK 11
  • Zulu8

いずれも、同じ文字コードを返す。

なぜUTF-8なのか?

これはEclipse実行時に指定されているのであった。

UTF-8になっているのは、これはEclipseの実行構成で指定されている文字コードである。

なのでEclipseの実行構成の設定をかえると、デフォルトの文字コードもかわる。

executesetting.png

VM引数の確認

以下のコードを追加して、JavaVMに渡されているVM引数も表示してみる。

RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
for (String vmArg : runtimeMxBean.getInputArguments()) {
	System.out.println(vmArg);
}

結果は、

-Dfile.encoding=UTF-8
java.version=1.8.0_202
java.vm.version=25.202-b08
java.vendor.url=https://adoptopenjdk.net/
file.encoding=UTF-8
sun.jnu.encoding=MS932
default encoding=UTF-8

Eclipseが実行時に -Dfile.encoding=UTF-8 を付与していることが分かる。

真のデフォルトの文字コード?

上記プログラムをコンソールから実行してみる。

set JAVA_HOME=C:\java\pleiades201812\java\8
mvnw clean package
%JAVA_HOME%\bin\java -jar target\java8learn.jar

Maven Wrapperでビルドして、実行可能jarを生成し、これを実行する。

すると、以下のような結果が得られる。

java.version=1.8.0_192
java.vm.version=25.192-b12
java.vendor.url=http://java.oracle.com/
file.encoding=MS932
sun.jnu.encoding=MS932
default encoding=windows-31j

VM引数は指定されていない。

windows-31jと表示されているが、これはMS932と同じものである。単なるAliasである。

これは、他のJava実行環境で試すと

  • Oracle JDK6
  • Oracle JDK8
  • AdoptOpenJDK 8
  • AdoptOpenJDK 11
  • Zulu8

いずれも、同じ文字コードが得られた。

なぜ、MS932なのか?

JAVAの文字コード設定は2つある。

  • file.encodingシステムプロパティ
  • sun.jnu.encodingシステムプロパティ1

このうち、file.encodingシステムプロパティが、デフォルトのCharsetとして使われている。

Charset.java抜粋

    /**
     * Returns the default charset of this Java virtual machine.
     *
     * <p> The default charset is determined during virtual-machine startup and
     * typically depends upon the locale and charset of the underlying
     * operating system.
     *
     * @return  A charset object for the default charset
     *
     * @since 1.5
     */
    public static Charset defaultCharset() {
        if (defaultCharset == null) {
            synchronized (Charset.class) {
                String csn = AccessController.doPrivileged(
                    new GetPropertyAction("file.encoding"));
                Charset cs = lookup(csn);
                if (cs != null)
                    defaultCharset = cs;
                else
                    defaultCharset = forName("UTF-8");
            }
        }
        return defaultCharset;
    }

では、このfile.encoding はどこで設定されているのか?

JVM起動時にSystemクラスの内部メソッドinitPropertiesが呼び出されてプロパティが設定されるが、 このinitPropertiesメソッドはNativeメソッドである。

これは、OpenJDK8のソースコードでは、以下のあたりに書かれている。

このソースを読むと

GetJavaPropertiesという関数で環境変数等々から、さまざまな情報を取得して、それをプロパティに設定していることがわかる。

文字コードまわりとしては、GetJavaProperties関数内から、Windowsの場合ではWin32 APIを使って 2

        /*
         * query the system for the current system default locale
         * (which is a Windows LCID value),
         */
        LCID userDefaultLCID = GetUserDefaultLCID();
        LCID systemDefaultLCID = GetSystemDefaultLCID();
        LCID userDefaultUILang = GetUserDefaultUILanguage();

といったコードで各種ロケールなどの情報を取得している。

このうち、GetUserDefaultLCID()file.encoding の元ネタになるようである。

関連するコードを少し抜き出して、日本語のWindows環境下で実行してみると

int main()
{
	LCID userDefaultLCID = GetUserDefaultLCID();
	wchar_t * ret = (wchar_t*) malloc(16 * 2);
	int codepage;

	if (GetLocaleInfo(userDefaultLCID,
		LOCALE_IDEFAULTANSICODEPAGE,
		ret + 2, 14) == 0) {
		codepage = 1252;
	}
	else {
		codepage = _wtoi(ret + 2);
	}
	printf("codepage=%d", codepage);
}

を実行すると、

codepage=932

という結果が返ってくる。

このGetUserDefaultLCIDで返されるコードページ932であれば「MS932」という文字列を作成し、これが file.encodingシステムプロパティに設定される。

つまり、JAVAのデフォルトの文字コードは可変である。

実行時の環境によってfile.encodingシステムプロパティは変わる。

明示的にfile.encodingシステムプロパティを指定した場合は、そちらが優先される。

なので、(たとえ同一バージョンのJAVAであっても)デフォルトの文字コードが特定の何かであるかを期待することはできない。

参考/元ネタ

以上、メモ終了。

Footnotes

  1. file.encoding がファイルの中身の文字コードである。対して、sun.jnu.encodingはファイル名の文字コードらしいが、具体的に、どのように使われているのかは、なかなか情報が見つからず不明である。

  2. Unix系の場合はC言語のsetlocale関数を使ってLC_CTYPE, LC_MESSAGEから判断している。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment