Java 8 UTF-8编码问题(Java错误?)

17

使用UTF-8编码创建字符串时存在不一致性。

运行此代码:

public static void encodingIssue() throws IOException {
    byte[] array = new byte[3];
    array[0] = (byte) -19;
    array[1] = (byte) -69;
    array[2] = (byte) -100;

    String str = new String(array, "UTF-8");
    for (char c : str.toCharArray()) {
        System.out.println((int) c);
    }
}

在Java 1.8.0_20(以及早期版本)中,我们得到了以下结果

 65533

在Java 1.7和1.6上,我们得到了正确的结果:

 57052

你遇到过这个错误吗?有没有解决方法?

对于 Shift_JIS、JIS_X0212-1990、x-IBM300、x-IBM834、x-IBM942、x-IBM942C、x-JIS0208 等编码也存在这种不一致的情况,但显然 UTF-8 更加紧急。

3个回答

11

这是 "Modified UTF-8" 编码的一个属性,可以像单个字符一样存储代理对(或该范围内的未配对字符)。如果声称使用标准 UTF-8 的解码器使用“Modified UTF-8”,则会出现错误。这似乎在Java 8中已经修复。

您可以可靠地使用指定使用“Modified UTF-8”的方法读取此类数据:

ByteBuffer bb=ByteBuffer.allocate(array.length+2);
bb.putShort((short)array.length).put(array);
ByteArrayInputStream bis=new ByteArrayInputStream(bb.array());
DataInputStream dis=new DataInputStream(bis);
String str=dis.readUTF();

感谢您的回复。事实上,我使用了一个遗留代码来修改UTF-8标准以解决Java 1.3的一个错误:http://developer.java.sun.com/developer/bugParade/bugs/4251997.html。您的代码片段解决了我的问题。 - Stefan Bulzan

5

Java 1.6/1.7接收到的值为U+DEDC(低代理项)。

来自RFC 3629

UTF-8的定义禁止编码U+D800和U+DFFF之间的字符编号,这些编号保留供UTF-16编码形式使用(作为代理对),并且不能直接表示字符。

...省略文本...

上述解码算法的实现必须防止解码无效序列。例如,一个天真的实现可能会将过长的UTF-8序列C0 80解码为字符U+0000,或将代理对ED A1 8C ED BE B4解码为U+233B4。解码无效序列可能会产生安全后果或引起其他问题。

Java 8将其解码为U+FFFD(替换字符)。这看起来像是Java 8中修复的错误。


非常感谢您的回复,它帮助我很好地理解了这个问题。我通过使用修改后的UTF-8来解码字节来修复了它。由于这是一个遗留代码,我必须保持向后兼容性。 - Stefan Bulzan

3

那是代理字符,对吧?虽然我不是Unicode专家,但我认为它本身没有意义。Java 8更改以支持Unicode 6.2。也许它在这方面更严格。65533是标准的0xFFFD替换字符,意思是“无法表示”。你有需要将其解释为字符串的实际情况吗?因为似乎Unicode表明它不再作为一个字符有意义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接