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

2022-09-01 23:10:34

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

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

这不会造成任何直接问题,适用于 s,因为总会有另外两个字节。虽然当涉及到单个变量时,与UTF-16编码相比,它具有16位的固定长度,但这些字符如何存储,特别是Java如何用其2字节“char”类型做到这一点?String


答案 1

答案就在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 代码单元用于作为 UTF-16 编码的代码单位的 16 位 char 值。有关 Unicode 术语的更多信息,请参阅 Unicode 词汇表。

简单地说:

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

更简单地说:

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

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


答案 2

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

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

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

编辑:如果你想在单个中存储一个非BMP代码点,你基本上就不走运了。这就像想要在变量中存储超过256个不同的值一样......它只是不起作用。按照在其他位置(例如 in )表示代码点的约定,最好只使用变量。charbyteStringint