Java如何在其16位字符类型中存储UTF-16字符?

根据Java SE 7规范 ,Java使用Unicode UTF-16标准来表示字符。 将String想象成一个包含一个字符的16位变量的简单数组时,生活很简单。

不幸的是,有16位的代码点是不够的(我相信它是所有Unicode字符的16/17)。 所以在String ,这没有直接问题,因为当想要使用额外的两个字节存储这些~1.048.576字符中的一个时,只需使用该String两个数组位置。

这没有任何直接问题,适用于String ,因为总有两个字节。 虽然单变量与UTF-16编码相比,具有16位固定长度 ,但如何存储这些字符,特别是Java如何使用2字节“char”来完成类型

答案是在javadoc中 :

char数据类型(以及Character对象封装的值)基于原始Unicode规范,该规范将字符定义为固定宽度的16位实体。 此后,Unicode标准已更改为允许表示forms需要16位以上的字符。

合法代码点的范围现在是U + 0000到U + 10FFFF,称为Unicode标量值。 (请参阅Unicode标准中U + n表示法的定义。)U + 0000到U + FFFF的字符集有时称为基本多语言平面(BMP)。 代码点大于U + FFFF的字符称为增补字符。 Java 2平台在char数组和String和StringBuffer类中使用UTF-16表示。 在此表示中,补充字符表示为一对char值,第一个来自高代理范围(\ uD800- \ uDBFF),第二个来自低代理范围(\ uDC00- \ uDFFF)。

因此,char值表示基本多语言平面(BMP)代码点,包括代理代码点或UTF-16编码的代码单元。 int值表示所有Unicode代码点,包括补充代码点。 int的较低(最低有效)21位用于表示Unicode代码点,而较高(最高有效)11位必须为零。

除非另有说明,否则有关补充字符和代理char值的行为如下:仅接受char值的方法不支持增补字符。 它们将代理范围中的char值视为未定义的字符。 例如,Character.isLetter(’\ uD840’)返回false,即使该字符串中的任何低代理值后面的特定值也表示字母。 接受int值的方法支持所有Unicode字符,包括补充字符。 例如,Character.isLetter(0x2F81A)返回true,因为代码点值表示一个字母(CJK表意文字)。 在Java SE API文档中,Unicode代码点用于U + 0000和U + 10FFFF之间范围内的字符值,Unicode代码单元用于16位字符值,这些值是UTF-16编码的代码单元。 有关Unicode术语的更多信息,请参阅Unicode术语表。

简单地说:

  • char规则的16位是为旧版Unicode标准设计的
  • 你有时需要两个字符来表示一个不在基本多语言平面中的unicode符文(代码点)。 这种“有效”,因为你不经常使用字符,尤其是处理BMP之外的unicode符文。

更简单的说:

  • java char不代表Unicode代码点(嗯,并非总是如此)。

顺便说一句,可以注意到,Unicode的演变超越了BMP,使得UTF-16在全球范围内无关紧要,因为UTF-16甚至不能实现固定的字节 – 字符比。 这就是更多现代语言基于UTF-8的原因。 这个宣言有助于理解它。

基本上,字符串存储一系列UTF-16代码单元……这与存储Unicode代码点序列不同。

当需要基本多语言平面之外的字符时,它在String中占用两个UTF-16代码单元。

大多数String操作 – length()charAtsubstring()等处理UTF-16代码单元的数量。 但是,像codePointAt()这样的操作将处理完整的Unicode代码点……尽管索引仍以UTF-16代码单位表示。

编辑:如果你想在一个char存储非BMP代码点,你基本上是运气不好。 这就像是想在一个byte变量中存储超过256个不同的值……它只是不起作用。 遵循在其他地方表示代码点的约定(例如在String ),最好只使用int变量。