我有一个简单的测试
@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-16LE
或UTF-16BE
来显式指定字节序,从而避免输出中的 BOM:
final byte[] bytes = test.getBytes("UTF-16BE");
UTF-16
字符集使用 16 位数量,因此对字节顺序很敏感。在这些编码中,流的字节顺序可以用由 Unicode 字符'uFEFF'
表示的初始字节顺序标记来表示。字节顺序标记的处理方式如下:
解码时,
UTF-16BE
和UTF-16LE
字符集将初始字节顺序标记解释为ZERO-WIDTH NON-BREAKING SPACE
;编码时,它们不写入字节顺序标记。解码时,
UTF-16
字符集解释输入流开头的字节顺序标记以指示流的字节顺序,但没有字节顺序标记,则默认为大端序;编码时,它使用大端字节顺序并写入大端字节顺序标记。