Java 1.6 Windows-1252编码在3个字符上失败

时间:2010-01-27 14:58:55

标签: java codepages windows-1252

编辑:我一直相信这个问题有些不合情理。感谢那些回应的人。我可以发布更具体的后续问题。

今天我投资了一些编码问题并编写了这个单元测试来隔离一个基本的repro案例:

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);

输出:

    Can't encode: 129 - Hi ? - encodes as: Hi ??
    Can't encode: 141 - Hi ? - encodes as: Hi ??
    Can't encode: 143 - Hi ? - encodes as: Hi ??
    Can't encode: 144 - Hi ? - encodes as: Hi ??
    Can't encode: 157 - Hi ? - encodes as: Hi ??
    Can't encode: 193 - Hi Á - encodes as: Hi ??
    Can't encode: 205 - Hi Í - encodes as: Hi ??
    Can't encode: 207 - Hi Ï - encodes as: Hi ??
    Can't encode: 208 - Hi ? - encodes as: Hi ??
    Can't encode: 221 - Hi ? - encodes as: Hi ??
    Bad count: 10

Mac OS 10.6.2上的JDK 1.6.0_07

我的观察:

Latin1对称编码所有254个字符。 Windows-1252没有。三个可打印字符(193,205,207)与Latin1和Windows-1252中的代码相同,因此我不希望出现任何问题。

任何人都可以解释这种行为吗?这是一个JDK错误吗?

- 詹姆斯

2 个答案:

答案 0 :(得分: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);
}

请注意,此测试程序使用(usnigned)字节值从1到255测试输入。问题中的代码使用 char值(相当于Unicode代码点)在此范围内)从1到255。

尝试打印示例中程序处理的实际字节数组,您会发现实际上并没有检查所有字节值,而且某些“坏”匹配与其他字符重复。

"Windows-1252"作为参数运行此命令会产生以下输出:

Can't encode: 129 - in: [-127]/ out: [63] - result: �
Can't encode: 141 - in: [-115]/ out: [63] - result: �
Can't encode: 143 - in: [-113]/ out: [63] - result: �
Can't encode: 144 - in: [-112]/ out: [63] - result: �
Can't encode: 157 - in: [-99]/ out: [63] - result: �
Bad count: 5

这告诉我们Windows-1252不接受字节值129,1441,143,144和157作为有效值。 (注意:我在这里谈论的是无符号字节值。上面的代码显示-127,-115,......因为Java只知道无符号字节)。

The Wikipedia article on Windows-1252似乎通过陈述这一点来验证这一观察结果:

  

根据微软和Unicode联盟网站上的信息,81,8D,8F,90和9D的位置尚未使用

答案 1 :(得分:2)

你的代码做了什么(String->byte[]->String,两次)几乎就是转码的相反,根本没有任何意义(几乎可以保证丢失数据)。转码意味着byte[]->String->byte[]

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

当然,当输入包含目标编码不支持的字符时,它将丢失数据。