UTF-16的意义是什么?

88

我从未理解UTF-16编码的意义。如果您需要将字符串视为随机访问(即代码点等同于代码单元),那么您需要使用UTF-32,因为UTF-16仍然是可变长度的。如果您不需要这个功能,那么与UTF-8相比,UTF-16似乎浪费了大量的空间。UTF-16相对于UTF-8和UTF-32的优点是什么,为什么Windows和Java将其用作本地编码?


也许您可以重新表达您的问题,使其不那么主观和争议性? - Gabe
5
如果UTF-32也是这样就好了……玩一下合成字符(http://en.wikipedia.org/wiki/Combining_character)五分钟,然后告诉我这一切有多“随机” :-) - xanatos
5个回答

64

当Windows NT被设计时,UTF-16不存在(NT 3.51诞生于1993年,而UTF-16是在1996年与Unicode 2.0标准一起诞生的),取而代之的是UCS-2。在那个时候,UCS-2足以容纳Unicode中所有可用的字符,因此1个代码点=1个代码单元的等价关系实际上是正确的——对于字符串没有需要变长逻辑。

后来他们转向UTF-16,以支持整个Unicode字符集;但是他们不能转向UTF-8或UTF-32,因为这将破坏API接口中的二进制兼容性(除其他事项外)。

至于Java,我不太确定;由于它是在大约1995年发布的,我怀疑UTF-16已经在蓬勃发展了(即使它还没有标准化),但我认为与基于NT的操作系统的兼容性可能在他们的选择中起到了一定的作用(每次调用Windows APIs时进行连续的UTF-8 <-> UTF-16转换可能会导致一些减速)。


编辑

维基百科解释说,即使对于Java也是同样的情况:它最初支持UCS-2,但在J2SE 5.0中转换到UTF-16。

因此,通常当您在某些API / Framework中看到使用UTF-16时,这是因为它最初作为UCS-2(为了避免字符串管理算法中的复杂性)开始,但它转向UTF-16以支持BMP之外的代码点,仍然保持相同的代码单元大小。


26

在UTF-8和UTF-16之间,除了向后兼容的回复,其他关于UTF-16优势的回复都没有任何意义。

我的评论有两个注意点。

Erik说:“UTF-16使用单元覆盖整个BMP - 所以除非你需要BMP外较稀有的字符,否则UTF-16实际上是每个字符2个字节。”

注意1)

如果您可以确信您的应用程序不需要BMP之外的任何字符,并且您为其编写的任何库代码将永远不会与需要BMP之外的字符的应用程序一起使用,则可以使用UTF-16,并编写使每个字符恰好为两个字节的隐式假设的代码。

这似乎极其危险(实际上很愚蠢)。

如果您的代码假定所有UTF-16字符均为两个字节长,并且您的程序与存在单个BMP之外字符的应用程序或库交互,则您的代码将出现错误。检查或操作UTF-16的代码必须编写为处理一个需要超过2个字节的UTF-16字符的情况;因此,我“忽略”了这个注意点。

UTF-16编码并不比UTF-8更易于编码(两者的代码都必须处理可变长度字符)。

注意2)

在某些情况下,如果适当编写,UTF-16可能会更具计算效率。

比如:假设某些长字符串很少被修改,但经常被检查(或更好地说,一旦构建就不再被修改 - 即一个创建不可修改字符串的字符串构建器)。为每个字符串设置一个标志,指示该字符串是否仅包含“固定长度”字符(即未包含长度不是两个字节的字符)。可以使用优化代码检查标志为true的字符串,该代码假定为固定长度(2字节)字符。

空间利用怎么样?

显然,UTF-16对于A)需要较少的字节数来编码而UTF-8需要较多字节的字符更有效率。

显然,对于UTF-8比UTF-16需要更少的字节来编码的B字符,UTF-8更加高效。

除了非常“专业”的文本外,B字符的数量很可能远远超过A字符的数量。


3
除了非常“专业”的文本外,B的数量很可能远远超过A的数量。大多数东亚地区可能会持不同意见,因为他们的语言大部分属于3字节UTF-8编码。 - Mark Tolonen
5
请参见 https://utf8everywhere.org/。即使在它们最糟糕的情况下,UTF-16只能节省20%的存储空间。如果存储空间对您很重要,您应该使用实际的压缩算法,而不是将其用作您的糟糕编码算法的借口。在绝大多数情况下,您将使用标记语言(如XML / HTML,JSON或Markdown)来格式化您的内容,这些标记语言都使用ASCII编码。 - yyny
那似乎非常危险(实际上是愚蠢的)。如果你的程序与一个应用程序或库进行交互,其中有一个超出BMP范围的单个字符,那么你的代码将会崩溃。这就是解决问题子集而不是整个问题(由于可能存在多种有效原因)的解决方案的一般性质。这并没有本质上的“极度危险”,更不用说“愚蠢”了。这被称为“权衡”。 - Sz.
@Sz。这是一个权衡,有很高的失败可能性,但获益微乎其微。如果您需要固定字节,请使用UTF-32;如果您需要最佳内存利用率,请使用UTF-8。需要稍微更好的内存大小,并且愿意对可能处理的文本做出大胆假设的情况非常少见,这似乎是一种非常愚蠢的方式,得到的收益微不足道,而潜在的损失却相当大。就像为了在飞行中多带一双鞋子而砍掉一条腿,而额外的行李箱只需支付40美元一样。 - Peter R
@PeterR 与那些使用生动夸张的词汇和情绪化的形容词来描述无聊的技术选择,却不承认可能存在合理的背景、特定需求和用途等,并且还称所有持不同意见者为愚蠢的人争论,是完全没有意义的。我只是希望将这一点记录下来,以便进行更加平衡的讨论。 - Sz.

5
UTF-16覆盖了整个BMP,使用单个单位 - 因此,除非您需要使用BMP外较罕见的字符,否则UTF-16每个字符有效地为2个字节。UTF-32占用更多空间,UTF-8需要支持可变长度。

我会添加必要的维基参考到UTF-32,其中解释了所有的缺点:http://en.wikipedia.org/wiki/UTF-32/UCS-4 - xanatos
24
“@Erik - 你可能会说UTF-8实际上是每个字符一个字节…除非你需要ASCII之外的罕见字符。实际上,UTF-16和UTF-8一样都是可变长度的。” - SigueSigueBen
我使用日语字符(或法语),我们正在考虑使用UTF-16。我希望这次讨论能包括变量的程度以及是否使用UTF-16可以更优化不同程度的非ASCII性。 - Aki
2
UTF-8覆盖了整个ASCII字符集,因此除非您需要更罕见的字符,否则UTF-8实际上是每个字符1个字节,而不是可变长度。 - Zdeněk Pavlas

3

UTF-16允许将所有基本多语言平面(BMP)表示为单个代码单元。 Unicode代码点超过U + FFFF由代理对表示。

有趣的是,Java和Windows(以及其他使用UTF-16的系统)都在代码单元级别而不是Unicode代码点级别上运行。因此,由单个字符U + 1D122(音乐符号F CLEF)组成的字符串在Java中被编码为“\ ud824 \ udd22”,并且“\ ud824 \ udd22” .length()== 2 (而不是 1 )。所以这有点像黑客,但事实证明字符不是可变长度的。

UTF-16相对于UTF-8的优势在于,如果使用UTF-8进行相同的黑客操作,则会放弃太多内容。


6
我认为,如果程序员必须了解可变长度字符,而不是“偶然”发现它们(如今的情况),世界将会更好。(现在,一个程序员可能要过多年才能知道一个代码点可以长达2个长度,但如果所有都是UTF-8编码,他只需要几个月就能掌握) - xanatos

0

UTF16通常用作多字节字符集的直接映射,即仅使用原始的0-0xFFFF分配的字符。

这为您提供了最佳的两个世界,您拥有固定的字符大小,但仍然可以打印任何人可能使用的所有字符(除了正统克林贡宗教脚本)。


8
除非他们来自香港,因为即使是基本的粤语句子也可能需要使用BMP以外的字符。此外,程序拒绝一些有效字符而原因却不为最终用户所知,这样做实在是太有趣了。 - prosfilaes
1
从今天开始,表情符号应该会普及到每个人,不受语言的限制 - 一个人只需期望/支持替代品。 - AmigoJack

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