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

3

I have a simple test

@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

我做错了什么?

https://en.wikipedia.org/wiki/UTF-16#Byte_order_encoding_schemes - JB Nizet
1个回答

10

如果你尝试:

final String test = "ппп";

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

-2
-1
4
63
4
63
4
63

-2是0xFE,-1是0xFF。它们一起形成了一个BOM (字节顺序标记)

在UTF-16中,BOM(U+FEFF)可以作为文件或字符流的第一个字符放置,以指示文件或流中所有16位代码单元的字节顺序。如果尝试使用错误的字节顺序读取此流,则字节将被交换,从而生成Unicode定义的“非字符” U+FFFE,不应出现在文本中。

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

您可以通过使用UTF-16LEUTF-16BE来明确指定字节顺序,从而避免输出中出现BOM:

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

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

  • 当解码时,UTF-16BEUTF-16LE 字符集将初始字节顺序标记解释为零宽度的非换行空格;当编码时,则不会写入字节顺序标记

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


我想强调一下,从Java 7开始,有一个名为java.nio.charset.StandardCharsets的类,它为经常使用的每个字符集提供了一组常量。它可以防止在字符集名称中出现所有的打字错误。暴露的字符集:
  • US-ASCII
  • ISO-8859-1
  • UTF-8
  • UTF-16BE
  • UTF-16LE
  • UTF-16
- Arnaud JOLLY

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