有啥理由比 UTF-8 更喜欢 UTF-16?
Posted
技术标签:
【中文标题】有啥理由比 UTF-8 更喜欢 UTF-16?【英文标题】:Is there any reason to prefer UTF-16 over UTF-8?有什么理由比 UTF-8 更喜欢 UTF-16? 【发布时间】:2011-02-25 11:05:34 【问题描述】:检查 UTF-16 和 UTF-8 的属性,我找不到任何理由更喜欢 UTF-16。
但是,查看 Java 和 C#,它看起来像那里的字符串和字符默认为 UTF-16。我在想这可能是出于历史原因,或者可能是出于性能原因,但找不到任何信息。
有人知道为什么这些语言选择 UTF-16 吗?我也有任何正当理由这样做吗?
编辑:同时我还找到了this answer,它看起来很相关并且有一些有趣的链接。
【问题讨论】:
除了我的回答之外,我想说 .NET/C# 选择 UTF-16 是因为这是 Windows 的“本机”编码:如果您使用相同的本机 Windows,则与本机 Windows 互操作会更容易编码。 您选择编码的目的是什么? UTF-16 是内存中字符串处理的合理选择,wchar_t
在 Windows 上将是 UTF-16,在其他地方通常是 UTF-32。但对于在线协议和文件存储,UTF-8 几乎总是最佳选择。
@codeka:我同意(给了你 +1),但也有人可能会问“为什么 Windows 的原生编码是 UTF-16 而不是 UTF-8?”。
qt c++ 框架也使用 utf-16 处理字符串
首选 UTF-16,如果它是您的操作系统或编程语言的本机。这主要是指 Windows、C# 和 Java。如果 UTF-8 是您的操作系统或编程语言的本机,或者您的编程语言实际上没有本机编码,请选择 UTF-8。这意味着 *nix 和 Mac OS X、C、C++。如果您从一开始就跨平台,那么在 C/C++ 的情况下,让 UTF-8 在 Windows 上正常工作似乎比在 *nix 上到处使用 UTF-16 更容易。 Perl 旨在处理所有编码,但隐式转换会导致许多错误。 javascript 包括 node.js 使用 UCS-2!!
【参考方案1】:
与 UTF-8(通常需要 3 个字节)相比,东亚语言通常需要 UTF-16 的存储空间(2 个字节足以存储 99% 的东亚语言字符)。
当然,对于西方语言,UTF-8 通常更小(1 个字节而不是 2 个)。对于像 html 这样的混合文件(其中有很多标记),这非常重要。
处理用户模式应用程序的 UTF-16略比处理 UTF-8 更容易,因为代理对的行为方式与组合字符的行为方式几乎相同。所以 UTF-16 通常可以作为固定大小的编码来处理。
【讨论】:
+1 用于正确表征 UTF-16 和 UTF-8 中每个字符的字节数。 我认为 UTF-8 最多可以编码 4 个字节,这几乎使 UTF-16 和 UTF-32 无用。 @Sir Psycho:UTF-8 是一种可变长度编码,处理起来比固定长度编码更复杂。另外,请参阅我对 Gumbo 的回答的 cmets:基本上,所有编码(UTF-8、UTF-16 和 UTF-32)中都存在组合字符,它们需要特殊处理。您可以使用与组合字符相同的特殊处理方法来处理 UTF-16 中的代理项对,因此在大多数情况下您可以忽略代理项并将 UTF-16 视为固定编码。 @Sir Psycho:UTF-8、UTF-16 和 UTF-32 都能够编码 Unicode 的所有字符。 codeka 正在讨论使用 UTF-8 和 UTF-16 对“典型”Unicode 字符进行编码会产生多少字节。 这里的关键字“可以通常被处理为固定大小的编码”。如果您关心字符的完整性,那么这样做仍然是绝对不正确的。您实际上所做的是编写代码来操作“字符”,但实际上是编写代码来操作“16 位数据块”。如果您要操作字符(交换、大写、反转等),那么您需要遵守字符编码的所有规则,而不仅仅是那些方便的规则。软件爆炸了,因为人们做出愚蠢的假设:(【参考方案2】:@Oak:评论太长了……
我不了解 C#(我会感到非常惊讶:这意味着他们只是复制了 Java 太多),但对于 Java,这很简单:Java 是在 Unicode 3.1 出现之前构思的。
因此代码点少于 65537 个,因此每个 Unicode 代码点仍然适合 16 位,因此 Java char 诞生了。
当然,这导致了今天仍然影响 Java 程序员(如我)的疯狂问题,您有一个方法 charAt 在某些情况下它既不返回 Unicode 字符也不返回 Unicode 代码点,并且一个方法(在 Java 5 中添加)codePointAt,它接受的参数不是您想要跳过的代码点数! (您必须向 codePointAt 提供要跳过的 Java char 的数量,这使其成为 String 类中最不被理解的方法之一。
所以,是的,这对大多数 Java 程序员来说绝对是疯狂和困惑的(大多数人甚至没有意识到这些问题),是的,这是出于历史原因。至少,这是当人们在这个问题后生气时想出的借口:但这是因为 Unicode 3.1 还没有出来。
:)
【讨论】:
【参考方案3】:我认为使用 UTF-16 的 C# 源自内部使用 UTF-16 的 Windows NT 系列操作系统。
我认为 Windows NT 内部使用 UTF-16 的主要原因有两个:
对于内存使用:UTF-32 浪费了一个 很多空间进行编码。 为了性能:UTF-8 更难 比 UTF-16 解码。在 UTF-16 中,字符是 一个基本多语言平面字符(2 个字节)或一个代理 对(4 个字节)。 UTF-8 字符 可以是 1 到 4 之间的任何位置 字节。与其他人的回答相反 - 您不能将 UTF-16 视为 UCS-2。如果要正确迭代字符串中的实际字符,则必须使用对 unicode 友好的迭代函数。例如,在 C# 中,您需要使用 StringInfo.GetTextElementEnumerator()
。
更多信息,wiki 上的这个页面值得一读:http://en.wikipedia.org/wiki/Comparison_of_Unicode_encodings
【讨论】:
哦,别忘了组合字符! (GetTextElementEnumerator
也会处理。)
"...您不能将 UTF-16 视为 UCS-2" - 但许多成功的现实世界应用程序都会这样做,并且侥幸逃脱,因为它们只使用 BMP 字符。
@Joe 对于简单的推送文本,假装每个字符是 2 个字节就可以“工作”(就像您经常假装 UTF-8 是 ASCII 并希望最好的结果一样)。事实上,这就是您在 C# 中使用 string
时通常所做的事情。但是,如果我以分解格式将一些文本粘贴或加载到您的应用程序中会发生什么?任何在逐个字符的基础上进行处理的东西都需要在实际了解该文本的编码方式的情况下进行。 (幸运的是,大多数应用程序都使用字符串,而不是字符。)
更大的原因是最初的 Windows NT 与 Unicode 1.1 几乎同时发布,在补充平面之前。
这里有深刻的思考,+1,现在让我们等待 UTF64 ;)【参考方案4】:
这取决于预期的字符集。如果您希望大量使用 7 位 ASCII 范围之外的 Unicode 代码点,那么您可能会发现 UTF-16 比 UTF-8 更紧凑,因为某些 UTF-8 序列的长度超过两个字节。
此外,出于效率原因,Java 和 C# 在索引字符串时不会考虑代理对。当使用由占用奇数字节的 UTF-8 序列表示的代码点时,这将完全崩溃。
【讨论】:
您能否详细说明“Java 和 C# 在索引字符串时不考虑代理对”? 如果您在 C#(或 Java)中有一个包含代理对的字符串(SP 用于对正常两字节范围之外的字符进行编码),每对将计为两个 16 位字符,而不是 1 个 Unicode 代码点。至少用于索引和长度报告目的。【参考方案5】:UTF-16 可以更有效地表示某些语言中的字符,例如中文、日语和韩语,其中 大多数 个字符可以用一个 16 位字表示。一些很少使用的字符可能需要两个 16 位字。 UTF-8 通常在表示来自西欧字符集的字符时效率更高 - UTF-8 和 ASCII 在 ASCII 范围 (0-127) 上是等效的 - 但对于亚洲语言来说效率较低,需要三个或四个字节来表示字符在 UTF-16 中可以用两个字节表示。
UTF-16 作为 Java/C# 的内存格式具有优势,因为基本多语言平面中的每个字符都可以用 16 位表示(请参阅 Joe 的回答)以及 UTF-16 的一些缺点(例如依赖于 \0 终止符的混淆代码)不太相关。
【讨论】:
【参考方案6】:如果我们仅讨论纯文本,UTF-16 在某些语言中可能更紧凑,日语(约 20%)和中文(约 40%)是最好的例子。当您比较 HTML 文档时,优势完全相反,因为 UTF-16 会为每个 ASCII 字符浪费一个字节。
至于简单性或效率:如果您在编辑器应用程序中正确实现 Unicode,复杂性将相似,因为 UTF-16 并不总是将代码点编码为单个数字,并且单个代码点通常不是分割文本的正确方法.
鉴于在最常见的应用程序中,UTF-16 不那么紧凑,并且实施起来同样复杂,因此更喜欢 UTF-16 而不是 UTF-8 的唯一原因是,如果您有一个完全封闭的生态系统,您可以在其中定期存储或完全在复杂的书写系统中传输纯文本,没有压缩。
用zstd或LZMA2压缩后,即使是100%的中文纯文本,优势也一扫而空;使用 gzip,UTF-16 的优势约为 4% 的中文文本,具有大约 3000 个独特的字素。
【讨论】:
【参考方案7】:对于许多(大多数?)应用程序,您将只处理 Basic Multilingual Plane 中的字符,因此可以将 UTF-16 视为固定长度编码。
因此,您可以避免 UTF-8 等可变长度编码的所有复杂性。
【讨论】:
+1 实际上我认为 Unicode 版本 1 只有基本的,这就是为什么许多平台认为 16 位对于简单的字符数据类型来说是正确的大小。 “我认为 Unicode 版本 1 只有基本的” - 是的,这是真的,更多细节在这里:en.wikipedia.org/wiki/UTF-16/UCS-2 这就像是说“很多程序只关心ASCII,所以可以把UTF-8当作定长编码。” 您当然不能将 UTF-16 视为定长编码。好吧,你可以,但这是错误的,在某些情况下它会失败。没有文本转换函数是代理中立的:计算字符长度、更改大小写、交换字符、反转字符串等……如果不考虑代理对,所有这些都可能导致字符损坏。您不能简单地忽略部分编码规则,因为它很方便或通常没有副作用。这是不正确的。 我经常被我发现的做出这个决定的新软件激怒,当文本中只有一个非 BMP 字符时会出现各种问题。它在以 Windows、Java、C# 和 JavaScript 为中心的 UCS-2/UTF-16 世界中尤为常见。以上是关于有啥理由比 UTF-8 更喜欢 UTF-16?的主要内容,如果未能解决你的问题,请参考以下文章
在 Perl 中有啥理由更喜欢 glob 而不是 readdir(反之亦然)?