为什么.net使用UTF16编码来处理字符串,但默认情况下使用UTF-8保存文件?

78

从这里开始

基本上,字符串使用UTF-16字符编码形式。

但是当保存时与StreamWriter相比:

此构造函数创建一个不带字节顺序标记(BOM)的UTF-8编码的StreamWriter,

我看过这个示例(已删除损坏的链接):

enter image description here

看起来对于某些字符串,utf8 更小,而对于另一些字符串,utf-16 更小。

  • 那么为什么 .net 使用 utf16 作为字符串的默认编码,而使用 utf8 来保存文件呢?

谢谢。

p.s. 我已经阅读了这篇著名的文章


1
Eric Lippert在这篇文章中进一步详细阐述了为什么做出这个决定。 - Lukazoid
@Lukazoid 很棒的帖子,但请注意评论区,Hans Passant提出了令人信服的反驳。 - Ohad Schneider
3
@Lukazoid的链接的工作版本:https://web.archive.org/web/20161121052650/http://blog.coverity.com/2014/04/09/why-utf-16/。 - Ian Kemp
简短的回答是UTF16不具备可移植性,而UTF8则非常具有可移植性。 - Zoltan Tirinda
3个回答

71
如果您可以忽略代理对(或者说您的应用程序不需要使用基本多语言平面之外的字符),那么UTF-16具有一些很好的特性,这主要是因为它始终需要两个字节表示一个编码单元,并且所有BMP字符都可以用单个编码单元表示。
考虑原始类型。如果我们使用UTF-8作为内存中的表示并希望处理所有 Unicode字符,那么大小应该是多少?它可能高达4个字节...这意味着我们必须始终分配4个字节。此时,我们不妨使用UTF-32!
当然,我们可以使用UTF-32作为 char 表示,但在 string 表示中使用UTF-8,在转换时进行转换。
UTF-16的两个缺点是:
  • 每个Unicode字符的代码单元数是可变的,因为并不是所有字符都BMP 中。直到表情符号变得流行起来,这并没有影响日常使用的许多应用程序。但现在,对于类似消息应用程序之类的应用,使用UTF-16的开发人员确实需要了解代理对。
  • 对于普通ASCII(至少在西方大部分文本是如此),它占用的空间是相当于等效UTF-8编码文本的两倍。
(作为一件小事情,我相信Windows使用UTF-16用于Unicode数据,这对于.NET遵循互操作性原理是有意义的。但这只是将问题推向前一步而已。)
鉴于代理对的问题,如果从头开始设计一种语言/平台并且没有互操作要求(但在Unicode方面基于其文本处理),那么UTF-16可能不是最佳选择。 UTF-8(如果您想要内存效率并且不介意一些处理复杂性以获得第n个字符)或UTF-32(反之亦然)可能是更好的选择。(即使获取第n个字符也存在“问题”,例如不同的规范化形式。文本很难...)

2
UTF-8的优点在于,如果您需要6个字节来真正表示所有可能性,那么任何少于UTF-32的编码都是需要特殊情况和额外代码的问题。因此,UTF-16和UTF-8都不完美。然而,由于UTF-8只有一半的大小,您最好使用它。使用UTF-16与之相比没有任何好处(除了增加文件/字符串大小)。当然,有些人会使用UTF-16并愚昧地认为它可以处理所有字符。 - gbjbaanb
3
我已经阅读了14次,但是我仍然不理解这句话的含义:“每个代码单元的大小保持不变”。据我所知,在UTF-16中,每个代码单元的大小可以是2、3或4字节,那么这里是什么保持不变的呢? - Royi Namir
1
@gbjbaanb:不,.NET使用UTF-16编码。因此,当需要使用BMP之外的任何字符时,会使用代理对(surrogate pairs)。每个字符都是一个UTF-16代码单元。(据我所知,UCS-16并不存在,我认为你指的是UCS-2。) - Jon Skeet
12
不,UTF-16代码单元的大小始终为2个字节。Unicode字符需要一个代码单元(用于基本多文种平面)或两个代码单元(用于U+10000及以上的字符)。 - Jon Skeet
1
@FernandoPelliccioni:您如何精确定义“可变宽度编码”?刚刚重新阅读了定义,我同意我对“代码单元”的确切含义感到困惑,但UTF-8和UTF-16在“它们可以采用可变数量的字节来表示单个Unicode代码点”方面都是可变宽度的。对于UTF-8,它是1-4个字节,对于UTF-16,它是2或4个字节。现在将检查我的答案的其余部分以获得精确性。 - Jon Skeet
显示剩余21条评论

41

对于许多“为什么选择这个”的问题,这是由历史决定的。 Windows在1993年成为Unicode操作系统的核心。当时,Unicode仍然只有65535个码位的编码空间,现在被称为UCS。直到1996年,Unicode才获得了补充平面来将编码空间扩展到一百万个码位。并使用代理对将它们适配到16位编码中,从而确立了utf-16标准。

.NET字符串使用utf-16,因为这与操作系统编码非常匹配,不需要进行转换。

utf-8的历史比较复杂。自Windows NT之后,RFC-3629可以追溯到1993年11月。它花了一段时间才在互联网上得以推广。


15

UTF-8是文本存储和传输的默认格式,因为对于大多数语言来说,它是一个相对紧凑的形式(有些语言在UTF-16中比UTF-8更紧凑)。每种具体语言都有更有效的编码方式。

UTF-16用于内存中的字符串,因为每个字符的解析速度更快,并且直接映射到Unicode字符类和其他表。Windows中的所有字符串函数都使用UTF-16,并已使用多年。


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