23

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

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

这在没有任何直接问题的情况下适用于Strings,因为总是可以有额外的两个字节。虽然当涉及到与 UTF-16 编码相比具有16 位固定长度的单个变量时,如何存储这些字符,特别是 Java 如何使用其2 字节“char”类型

4

2 回答 2

26

答案在javadoc中:

char 数据类型(以及因此 Character 对象封装的值)基于原始 Unicode 规范,该规范将字符定义为固定宽度的 16 位实体。此后,Unicode 标准已更改为允许表示需要超过 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 位 char 值,它们是 UTF-16 编码的代码单元。有关 Unicode 术语的更多信息,请参阅 Unicode 词汇表。

简单地说:

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

更简单的说:

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

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

于 2012-10-28T20:00:57.800 回答
8

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

当需要基本多语言平面之外的字符时,它会占用String.

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

编辑:如果您想将非 BMP 代码点存储在单个char中,那么您基本上不走运。这就像想在一个变量中存储超过 256 个不同的值byte......它只是行不通。遵循在其他地方(例如 in String)表示代码点的约定,最好只使用int变量。

于 2012-10-28T20:01:17.160 回答