为什么使用utf-16编码,此unicode字符最终以6个字节结尾?

时间:2019-01-04 11:59:03

标签: java unicode

我正在使用来自this question的可接受答案的代码片段。我只是添加了一个字节数组来使用UTF-16,如下所示:

final char[] chars = Character.toChars(0x1F701);
final String s = new String(chars);
final byte[] asBytes = s.getBytes(StandardCharsets.UTF_8);
final byte[] asBytes16 = s.getBytes(StandardCharsets.UTF_16);

chars有2个元素,这意味着Java中有两个16位整数(因为代码点在BMP之外)。

asBytes有4个元素,对应于32位,这是我们需要从chars表示两个16位整数的原因,因此这很有意义。

asBytes16有6个元素,这使我感到困惑。为什么当32位足以表示这个unicode字符时,我们为什么要额外增加2个字节?

2 个答案:

答案 0 :(得分:5)

UTF-16字节以Byte order mark FEFF开头,指示该值以big-endian编码。根据Wiki,BOM还用于区分UTF-16和UTF-8:

  

这两个序列都不是有效的UTF-8,因此它们的存在表明该文件未使用UTF-8编码。

您可以按照this answerbyte[]转换为十六进制编码的String

asBytes   = F09F9C81
asBytes16 = FEFFD83DDF01

答案 1 :(得分:3)

  

asBytes有4个元素,对应于32位,这是我们需要从chars表示两个16位整数的原因,所以这很有意义。

实际上没有,用Java表示codepoint所需的char数量与此无关。字节数与代码点本身的数值直接相关。

代码点U + 1F701(0x1F701)使用17位(11111011100000001

0x1F701需要UTF-8(F0 9F 9C 81)中的4个字节来对其17位进行编码。请参见Wikipedia上的位分布图。该算法在RFC 3629中定义。

  

asBytes16有6个元素,这让我感到困惑。为什么当32位足以表示这个unicode字符时,我们为什么要额外增加2个字节?

根据StandardCharsets的Java文档

  

UTF_16

public static final Charset UTF_16
     

16位UCS转换格式,由可选字节顺序标记标识的字节顺序

0x1F701需要UTF-16(D8 3D DF 01)中的4个字节来对其17位进行编码。请参见Wikipedia上的位分布图。该算法在RFC 2781中定义。

与UTF-8不同,UTF-16受endian约束,因此StandardCharsets.UTF_16包含一个BOM来指定字节数组中使用的实际字节序。

为避免BOM,请根据需要使用StandardCharsets.UTF_16BEStandardCharsets.UTF_16LE

  

UTF_16BE

public static final Charset UTF_16BE
     

16位UCS转换格式,大端字节顺序

     

UTF_16LE

public static final Charset UTF_16LE
     

16位UCS转换格式,小尾数字节顺序

由于名称中暗含了字节序,因此它们不需要在字节数组中包含BOM。