UTF-16拆分为2个字符

3
我一直认为Java在内部使用UTF-16来编码其字符。这得到了证实,因为它使用u+xxxx格式表示字符代码,并且使用16位来存储char。
但有时候UTF-16需要超过2个字节。在这种情况下,Java需要使用2个char来表示1个UTF-16字符。
顺便说一下:这让我想知道是否更正确地说“Java只支持Unicode字符集,并使用16位单元格存储字符”。
问题:第一个char是否提供某种方法来确定是否使用第二个char,或者这两个char属于一起?
1个回答

7
是的,当Unicode从Unicode 1.0扩展到今天的1114112个代码点限制时,UTF-16被发明出来。这使它能够支持整个通用字符集,并与UCS-2保持兼容性;所有Unicode字符作为双字节单位的过时编码方式,正是因为它无法在Unicode 2.0或更高版本中编码所有Unicode字符而过时。
在UTF-16中,两个字节的单位要么是:
1. 高代理项,后面必须跟随一个低代理项。在0xD800和0xDBFF之间(包括边界),isHighSurrogate将返回true。 2. 低代理项,必须跟随一个高代理项。在0xDC00和0xDFFF之间(包括边界),isLowSurrogate将返回true。 3. 非代理项。
非代理项直接映射到相同代码点的BMP字符。
代理项结合起来表示星际飞船字符:
1. 从代码点中减去0x010000。 2. 将前10位加上0xD800得到高代理项。 3. 将后10位加上0xDC00得到低代理项。
在Java中,您可以通过首先在代码点上检查isBmpCodePoint来执行此操作。如果返回true,则可以将其强制转换为char以获取编码它的单个UTF-16单位。否则,您可以调用highSurrogate来获取第一个char,调用lowSurrogate来获取第二个char。
除了isBmpCodePoint之外,您还可以使用charCount。对于BMP字符,它返回1;如果需要代理项,则返回2。如果要创建一个包含值的字符数组,则这很有用。
由于代理项代码点从未被分配给字符,因此这意味着编码对于整个通用字符集是无歧义的。
它也是自我纠正的,流中的错误可以被隔离而不会导致所有后续字符被错误读取。例如,如果我们发现一个孤立的低代理项,我们知道该位是错误的,但仍然可以读取流中的其余部分。

以下是一些完整的示例,但我对Java不太熟悉(另一方面,我了解Unicode,并且这就是我用来回答这个问题的知识),所以如果有人发现一个n00b Java错误,但认为我在Unicode知识部分正确,请随时编辑此帖子:

""是一个字符串,其中包含一个Unicode字符U+10300,它是古意大利字母表中的一个字母。在大多数情况下,这些“星际平面”字符因为Unicode联盟试图尽可能有用而不超出更易使用的BMP(基本多语言平面;U+0000U+FFFF,虽然有时列为"U+0000U+FFFD,因为U+FFFEU+FFFF都是非字符,在大多数情况下不应使用)而相对较少见。

(如果您正在进行实验,则直接使用的那些将取决于您的文本编辑器处理它的能力)。

如果您检查"".length,您将得到2,因为length给出的是UTF-16编码单元的数量,而不是字符的数量。

new StringBuilder().appendCodePoint(0x10300).toString() == ""应该返回true

Character.charCount(0x10300)将返回2,因为我们需要两个UTF-16 char来编码它。Character.isBmpCodePoint(0x10300)将返回false

Character.codePointAt("", 0)将返回66304,这是0x10300,因为当它看到高代理项时,它包括在计算中读取以下低代理项。

Character.highSurrogate(0x10300) == 0xD800 && Character.lowSurrogate(0x10300) == 0xDF00是正确的,因为这些是字符应该被分割为以在UTF-16中进行编码的高代理项和低代理项。

同样地,"".charAt(0) == 0xD800 && "".charAt(1) == 0xDF00,因为charAt处理的是UTF-16单元,而不是Unicode字符。

同样地,"" == "\uD800\uDF00",它使用转义序列来表示两个代理项。


我认为提问者指的是Java原始类型char,而不是UTF-16代理项。在这种情况下,没有将2个字符链接在一起的概念。这样的字符不能存储在Java原始char中。 - user1886323
其实你是对的 - 即使是超过U+FFFF的字符的String实现也是由char对支持的。 - user1886323
谢谢,我猜Character.isHighSurrogate(char)可以用来验证这个字符是否与下一个char相连。 - bvdb
好的,我会稍微补充一下。 - Jon Hanna
你能举个例子来说明代理字符如何组合表示天界字符吗?我猜你指的是像♥这样的字符。 - bvdb
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接