Uncategorized

Files#readStringでShift_JISで(UTF-8以外の多くのエンコーディングで?)保存されたテキストファイルをJava 22で読み込むと化ける現象とその回避策

JDK 22がリリースされました。早速アップデートしようと思ったらテストが一つ落ちることに気が付きました。調べたところ、Shift_JISのファイル読み込みで文字化けが発生していることがわかりました。Java 22のバグのような気がします。以下のコードで再現できます。

package one.cafebabe;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;

public class Main {
    public static void main(String[] args) throws IOException {
        System.out.println("Java Version:" + System.getProperty("java.version"));

        String[] encodings = {"UTF-8", "SJIS"};
        for (String encoding : encodings) {
            test(encoding);
        }
    }

    static void test(String encoding) throws IOException {
        System.out.println("---------Testing:" + encoding);
        Charset charset = Charset.forName(encoding);
        Path path = Path.of(encoding + "_encoded.txt");
        String originalContent = "日本語です";
        byte[] encodedBytes = originalContent.getBytes(charset);
        String decodedString = new String(encodedBytes, charset);
        System.out.println(decodedString);
        System.out.println("Decoded string matches:" + decodedString.equals(originalContent));

        Files.writeString(path, originalContent, charset);
        String read = Files.readString(path, charset);
        System.out.println(read);
        boolean matches = read.equals(originalContent);
        System.out.println("Content from file matches:" + matches);

    }
}

“日本語です”という文字列をShift_JISでファイルに書き出し、読み込むだけです。

Java 22だと実行結果が以下のようになります。バイト列で書き出して、new String()で復元した場合は問題ありませんが、Files.readStringで読み込んだ場合は化けているのが分かります。

Java Version:22
日本語です
Decoded string matches:true
åe,gžŠg0Y0
Content from file matches:false

“日本語です”という文字列をShift_JISでファイルに書き出し、読み込むだけです。

Java 21は以下のように化けません。

Java Version:21.0.2
日本語です
Decoded string matches:true
日本語です
Content from file matches:true

Java 22は長期サポート版(LTS)ではないので、移行する人は多くないと思いますが、日本で広く使われるエンコーディングなのでご利用になる方はご注意ください。UTF-8エンコーディングの読み込みでは文字化けしません。また、Shift_JIS以外については確認していません。
根本原因について調査はしません。興味のある方は是非追ってみてください。

この現象は既にbugreport.java.comへ報告済みです。

確認したところShift_JISに限らず、UTF-8以外の多くのエンコーディングで文字化けするようです。

さらに確認したところ、InputStreamReaderを使った場合は化けず、Files#readStringを使った場合に化けることも分かりました。new String()で化けていなかったことからそこまで心配していませんでしたが、コアライブラリ全般に渡るバグではなくて、ピンポイントにFiles#readStringの問題かもしれませんね。

追記:

バグ登録されました。

JDK-8328714 : Shift_JIS encoded content gets garbled with Java 22

追記:

Oracleから返事があり、リリースノートに記載の既知の問題であるとのことです。リリースノートを確認するのは基本ですね。お恥ずかしい!しかしこんな問題がテストで検出されず、既知の問題として認識されたままリリースされるとは想像していませんでした……。というわけで、リリースノートに記載の通り、-XX:-CompactStrings オプションをつけることで回避が出来ることを確認しました。ちなみに@skrbにより指摘していただいた件がビンゴでした(この修正がリグレッションを産んだのでは?という予想だったみたいですが、まだ取り込まれていなかったようです)。