4

編集:私はこの質問がやや無意味であると確信しています。回答してくださった方々に感謝します。より具体的なフォローアップの質問を投稿する場合があります。

今日、私はいくつかのエンコーディングの問題に投資し、基本的な再現ケースを分離するためにこの単体テストを作成しました。

int badCount = 0;
for (int i = 1; i < 255; i++) {
    String str = "Hi " + new String(new char[] { (char) i });

    String toLatin1  = new String(str.getBytes("UTF-8"), "latin1");
    assertEquals(str, new String(toLatin1.getBytes("latin1"), "UTF-8"));

    String toWin1252 = new String(str.getBytes("UTF-8"), "Windows-1252");
    String fromWin1252 = new String(toWin1252.getBytes("Windows-1252"), "UTF-8");

    if (!str.equals(fromWin1252)) {
        System.out.println("Can't encode: " + i + " - " + str + 
                           " - encodes as: " + fromWin1252);
        badCount++;
    }
}

System.out.println("Bad count: " + badCount);

出力:

    エンコードできません:129-こんにちは?-次のようにエンコードします:こんにちは??
    エンコードできません:141-こんにちは?-次のようにエンコードします:こんにちは??
    エンコードできません:143-こんにちは?-次のようにエンコードします:こんにちは??
    エンコードできません:144-こんにちは?-次のようにエンコードします:こんにちは??
    エンコードできません:157-こんにちは?-次のようにエンコードします:こんにちは??
    エンコードできません:193-こんにちはÁ-次のようにエンコードします:こんにちは??
    エンコードできません:205-こんにちはÍ-次のようにエンコードします:こんにちは??
    エンコードできません:207-こんにちはÏ-次のようにエンコードします:こんにちは??
    エンコードできません:208-こんにちは?-次のようにエンコードします:こんにちは??
    エンコードできません:221-こんにちは?-次のようにエンコードします:こんにちは??
    悪いカウント:10

MacOS10.6.2上のJDK1.6.0_07

私の観察:

Latin1は、254文字すべてを対称的にエンコードします。Windows-1252はそうではありません。印刷可能な3文字(193、205、207)は、Latin1とWindows-1252で同じコードであるため、問題は発生しないと思います。

誰かがこの振る舞いを説明できますか?これはJDKのバグですか?

-ジェームズ

4

2 に答える 2

4

私の意見では、テストプログラムは、意味的な意味を持たない文字列間で効果的に役に立たない変換を行うため、深刻な欠陥があります。

すべてのバイト値が特定のエンコーディングに対して有効な値であるかどうかを確認する場合は、次のようになります。

public static void tryEncoding(final String encoding) throws UnsupportedEncodingException {
    int badCount = 0;
    for (int i = 1; i < 255; i++) {
        byte[] bytes = new byte[] { (byte) i };

        String toString = new String(bytes, encoding);
        byte[] fromString = toString.getBytes(encoding);

        if (!Arrays.equals(bytes, fromString)) {
            System.out.println("Can't encode: " + i + " - in: " + Arrays.toString(bytes) + "/ out: "
                    + Arrays.toString(fromString) + " - result: " + toString);
            badCount++;
        }
    }

    System.out.println("Bad count: " + badCount);
}

このテストプログラムは、1〜255の(使用された)バイト値を使用して入力をテストすることに注意してください。問題のコードは、1〜255のchar値(この範囲のUnicodeコードポイントに相当)を使用します。

例のプログラムによって処理される実際のバイト配列を出力してみてください。実際にすべてのバイト値をチェックしているわけではなく、「不良」一致の一部が他の一致と重複していることがわかります。

"Windows-1252"引数としてこれを実行すると、次の出力が生成されます。

エンコードできません:129-入力:[-127] /出力:[63]-結果:�
エンコードできません:141-入力:[-115] /出力:[63]-結果:�
エンコードできません:143-入力:[-113] /出力:[63]-結果:�
エンコードできません:144-入力:[-112] /出力:[63]-結果:�
エンコードできません:157-入力:[-99] /出力:[63]-結果:�
悪いカウント:5

これはWindows-1252、バイト値129、1441、143、144、および157を有効な値として受け入れないことを示しています。(注:ここでは符号なしバイト値について話します。Javaは符号なしバイトしか認識しないため、上記のコードは-127、-115、...を示しています)。

Windows-1252に関するウィキペディアの記事は、次のように述べてこの観察結果を検証しているようです。

MicrosoftおよびUnicodeConsortiumのWebサイトの情報によると、位置81、8D、8F、90、および9Dは使用されていません。

于 2010-01-27T15:12:59.453 に答える
2

コードが行うこと(String->byte[]->String、2回)は、トランスコーディングとはほとんどであり、まったく意味がありません(データが失われることが事実上保証されています)。トランスコーディングとはbyte[]->String->byte[]

public byte[] transcode(byte[] input, String inputEnc, String targetEnc)
{
    return new String(input, inputEnc).getBytes(targetEnc);
}

そしてもちろん、ターゲットエンコーディングがサポートしていない文字が入力に含まれている場合、データは失われます。

于 2010-01-27T15:25:13.057 に答える