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

2022-09-03 01:56:49

使用 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更为紧迫。


答案 1

它是Modified UTF-8编码的一个属性,用于存储代理项对(甚至是该范围的未配对字符),如单个字符。如果声称使用标准的解码器使用“修改后的UTF-8”,这是一个错误。这似乎已经在Java 8中修复了。UTF-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();

答案 2

在 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中修复的错误。


推荐