为啥 .net 对字符串使用 UTF16 编码,但默认使用 UTF-8 来保存文件?

Posted

技术标签:

【中文标题】为啥 .net 对字符串使用 UTF16 编码,但默认使用 UTF-8 来保存文件?【英文标题】:Why does .net use the UTF16 encoding for string, but uses UTF-8 as default for saving files?为什么 .net 对字符串使用 UTF16 编码,但默认使用 UTF-8 来保存文件? 【发布时间】:2013-02-03 05:24:37 【问题描述】:

From here

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

但是当保存 vs StreamWriter 时:

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

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

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

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

谢谢。

附言我已经读过the famous article

【问题讨论】:

来自 Eric Lippert 的 This post 详细说明了做出此决定的原因。 @Lukazoid 很棒的帖子,但请注意 cmets,Hans Passant 不同意有说服力的论点。 @Lukazoid 链接的工作版本:web.archive.org/web/20161121052650/http://blog.coverity.com/… 简短的回答是 UTF16 是不可移植的,而 UTF8 是超级便携的。 【参考方案1】:

如果您乐于忽略代理对(或等效地,您的应用可能需要基本多语言平面之外的字符),UTF-16 有一些不错的属性,主要是因为始终需要两个每个代码单元的字节数,每个代码单元表示所有 BMP 字符。

考虑基本类型char。如果我们使用 UTF-8 作为内存中的表示并想要处理 all 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 个字符也有“问题”。文本很难......)

【讨论】:

UTF-8 的要点是,如果您需要每个字符 6 个字节来真正表示所有可能性,那么任何小于 UTF-32 的内容都是需要特殊情况和额外代码的问题。所以 UTF-16 和 UTF-8 都是不完美的。但是,由于 UTF-8 是大小的一半,您不妨使用它。在它上面使用 UTF-16 将一无所获(增加的文件/字符串大小除外)。当然,有些人会使用 UTF-16 并无知地认为它可以处理所有字符。 我已经读了 14 遍了。我仍然不明白这一行:每个代码单元的大小是恒定的。 AFAIK 大小可以是 2,3,4 字节(在 utf-16 中),那么这里的常量是什么? @gbjbaanb:不,.NET 使用 UTF-16。因此,当需要 BMP 之外的任何内容时,将使用代理对。每个字符都是一个 UTF-16 代码单元。 (据我所知,也没有 UCS-16 之类的东西 - 我认为您的意思是 UCS-2。) @RoyiNamir:不,UTF-16 代码单元的大小总是 2 个字节。一个 Unicode 字符采用一个代码单元(对于基本多语言平面)或两个代码单元(对于字符 U+10000 及以上)。 @FernandoPelliccioni:您如何准确定义“可变宽度编码”?刚刚重读定义,我同意我对“代码单元”的确切含义感到困惑,但 UTF-8 和 UTF-16 都是可变宽度,因为“它们可以采用可变数量的字节来表示单个 Unicode 代码点”。对于 UTF-8,它是 1-4 个字节,对于 UTF-16,它是 2 或 4。现在将检查我的其余答案是否精确。【参考方案2】:

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

.NET 字符串是 utf-16,因为它非常适合操作系统编码,不需要转换。

utf-8 的历史更加模糊。毫无疑问,在 Windows NT 之后,RFC-3629 可以追溯到 1993 年 11 月。它需要一段时间才能站稳脚跟,互联网发挥了重要作用。

【讨论】:

【参考方案3】:

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

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

【讨论】:

以上是关于为啥 .net 对字符串使用 UTF16 编码,但默认使用 UTF-8 来保存文件?的主要内容,如果未能解决你的问题,请参考以下文章

转换编码 --GBK---UTF16---UTF8

python为啥中文显示的都是16进制的?

使用C ++将越南语字符编码为USASCII,ISO88591,UTF8,UTF16BE,UTF16LE,UTF16

刨根究底字符编码之十三——UTF-16编码方式

2019年9月 前端下载和上传总结 blob的使用

字节序的问题,为啥GBK和UTF-8没有字节序问题,而UTF-16就有?