我正在使用来自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个字节?
答案 0 :(得分:5)
UTF-16字节以Byte order mark FEFF
开头,指示该值以big-endian编码。根据Wiki,BOM还用于区分UTF-16和UTF-8:
这两个序列都不是有效的UTF-8,因此它们的存在表明该文件未使用UTF-8编码。
您可以按照this answer将byte[]
转换为十六进制编码的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_16BE
或StandardCharsets.UTF_16LE
:
UTF_16BE
public static final Charset UTF_16BE
16位UCS转换格式,大端字节顺序
UTF_16LE
public static final Charset UTF_16LE
16位UCS转换格式,小尾数字节顺序
由于名称中暗含了字节序,因此它们不需要在字节数组中包含BOM。