Java UTF-16 字符串始终使用 4 个字节而不是 2 个字节



我有一个简单的测试

@Test
public void utf16SizeTest() throws Exception {
final String test = "п";
// 'п' = U+043F according to unicode table
// 43F to binary = 0100 0011 1111 (length is 11)
// ADD '0' so length should be = 16
// 0000 0100 0011 1111
// 00000100(2) 00111111(2)
//    4(10)  63(10)
final byte[] bytes = test.getBytes("UTF-16");
for (byte aByte : bytes) {
System.out.println(aByte);
}
}

如您所见,我首先将"п"转换为二进制,然后在length != 16时添加尽可能多的空咬。

期望输出将4 , 63

但实际的是一个:

-2
-1
4
63

我做错了什么?

如果您尝试:

final String test = "ппп";

你会发现-2 -1只出现在开头:

-2
-1
4
63
4
63
4
63

-2 是0xFE,-1 是0xFF。 它们共同构成了一个BOM (Byte_order_mark)

在 UTF-16 中,BOM (U+FEFF) 可以作为 文件或字符流,以指示字节顺序 文件或流的所有 16 位代码单元。如果进行了尝试 要以错误的字节序读取此流,字节将是 交换,从而提供字符 U+FFFE,由 Unicode 作为"非字符",不应出现在文本中。

test.getBytes("UTF-16");在对字节进行编码时默认使用大端序,因此前面包含一个 BOM,以便以后的处理器可以知道使用了大端序。

您可以通过改用UTF-16LEUTF-16BE来显式指定字节序,从而避免输出中的 BOM:

final byte[] bytes = test.getBytes("UTF-16BE");

UTF-16字符集使用 16 位数量,因此对字节顺序很敏感。在这些编码中,流的字节顺序可以用由 Unicode 字符'uFEFF'表示的初始字节顺序标记来表示。字节顺序标记的处理方式如下:

  • 解码时,UTF-16BEUTF-16LE字符集将初始字节顺序标记解释为ZERO-WIDTH NON-BREAKING SPACE;编码时,它们不写入字节顺序标记

  • 解码时,UTF-16字符集解释输入流开头的字节顺序标记以指示流的字节顺序,但没有字节顺序标记,则默认为大端序;编码时,它使用大端字节顺序并写入大端字节顺序标记

最新更新