java.lang.String#getBytes() の動作仕様

Unicode から Shift_JIS(CP932) に変換するときの java.lang.Stringクラス getBytesメソッドの動作仕様について。

例として、"♨ B" のUTF-8のバイト列を Shift_JIS(CP932) のバイト列に変換する処理を Java コードで示す。

public class Main {
    public static void main(String[] args) {
        try {
            byte[] utf8_byte_array = { (byte)0xe2, (byte)0x99, (byte)0xa8, (byte)0x20, (byte)0x42 }; // "♨ B" のUTF-8のバイト列
            String str = new String(utf8_byte_array, "UTF-8");  // UTF-8 のバイト列を String オブジェクトに変換
            byte[] sjis_byte_array = str.getBytes("MS932");   // String オブジェクトを Shift_JIS(CP932) のバイト列に変換

            System.out.println(str);    // String オブジェクトを表示
            printByteArray(utf8_byte_array);    // UTF-8 のバイト列を16進数で表示
            printByteArray(sjis_byte_array);    // Shift_JIS(CP932) のバイト列を16進数で表示
        } catch (Exception e) {
            // do nothing
        }
    }

    private static void printByteArray(byte[] byte_array) {
        for (int i=0; i<byte_array.length; i++) {
            System.out.print( String.format("%02x ", byte_array[i]) );
        }
        System.out.println();
    }
}

コンパイルする

$ javac Main.java

実行結果は下記となる。

$ java Main
♨ B
e2 99 a8 20 42 
3f 20 42 

「♨」 という文字は Unicode のコードポイント U+2668 に定義されるが、Shift_JIS(CP932) の文字集合には存在しないため代替文字「?」(16進数の3f)に置換されている。

疑問

デフォルトの代替文字「?」を別の文字にすることは可能か?

できそう。

String#getBytes()やnew String()は、内部ではCharsetDecoderやCharsetEncoderクラスが使われている。これらを直接使えば、変換できない文字コードを「?」にせずにエラーにする、といった処理を実装できる。