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 - Hi Á - 编码为:Hi ??
    无法编码:205 - Hi Í - 编码为:Hi ??
    无法编码:207 - Hi Ï - 编码为:Hi ??
    无法编码:208 - 嗨?- 编码为:嗨??
    无法编码:221 - 嗨?- 编码为:嗨??
    坏数:10

Mac OS 10.6.2 上的 JDK 1.6.0_07

我的观察:

Latin1 对称地编码所有 254 个字符。Windows-1252 没有。三个可打印字符(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 作为有效值。(注意:我在这里谈论的是无符号字节值。上面的代码显示了 -127、-115 ……因为 Java 只知道无符号字节)。

关于 Windows-1252 的 Wikipedia 文章似乎通过陈述以下内容来验证这一观察:

根据 Microsoft 和 Unicode Consortium 网站上的信息,未使用位置 81、8D、8F、90 和 9D

于 2010-01-27T15:12:59.453 回答
2

您的代码所做的(String->byte[]->String, 两次)与转码几乎相反,根本没有意义(几乎可以保证丢失数据)。转码意味着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 回答