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

时间:2018-07-08 08:35:55

标签: java string unicode character utf-16

我有一个简单的测试

@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

我在做什么错了?

1 个答案:

答案 0 :(得分:6)

如果您尝试:

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");在对字节进行编码时默认使用Big Endian,因此BOM包含在前面,以便以后的处理器可以知道使用了Big Endian。

您可以改为使用UTF-16LE or UTF-16BE来显式指定字节序,从而避免在输出中使用BOM:

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

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

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

  •   
  • 在解码时,UTF-16字符集解释输入流开头的字节顺序标记以指示流的字节顺序,但如果没有字节顺序,则默认为big-endian。订单标记; 编码时,它使用big-endian字节顺序并写入一个big-endian字节顺序标记

  •