为什么 C# Unicode 范围覆盖有限范围(最多 0xFFFF)?

No1*_*ver 0 c# unicode encoding utf-8

我对 C# UTF8 编码感到困惑......

假设这些“事实”是正确的:

  1. Unicode 是定义每个字符的“协议”。
  2. UTF-8 定义了“实现”——如何存储这些字符。
  3. Unicode 定义字符范围从 0x0000 到 0x10FFFF(来源

根据C# 参考,每个字符的可接受范围是 0x0000 到 0xFFFF。我不明白 0xFFFF 以上并在 Unicode 协议中定义的另一个字符怎么样?

与 C# 相比,当我使用 Python 编写 UTF8 文本时,它覆盖了所有预期范围(0x0000 到 0x10FFFF)。例如:

u"\U00010000"  #WORKING!!!
Run Code Online (Sandbox Code Playgroud)

这不适用于 C#。更重要的是,当我用Python将字符串u"\U00010000"(单个字符)写入文本文件,然后从C#中读取它时,这个单字符文档在C#中变成了2个字符!

# Python (write):
import codecs                        
with codes.open("file.txt", "w+", encoding="utf-8") as f:                        
    f.write(text) # len(text) -> 1

// C# (read): 
string text = File.ReadAllText("file.txt", Encoding.UTF8); // How I read this text from file.
Console.Writeline(text.length); // 2
Run Code Online (Sandbox Code Playgroud)

为什么?怎么修?

bob*_*nce 5

\n

根据 C# 参考,每个字符的可接受范围是 0x0000 到 0xFFFF。我不明白 0xFFFF 以上并在 Unicode 协议中定义的另一个字符怎么样?

\n
\n\n

不幸的是,C#/.NETchar不表示 Unicode 字符。

\n\n

Achar是 0x0000 到 0xFFFF 范围内的 16 位值,代表一个 \xe2\x80\x9cUTF-16 代码单元\xe2\x80\x9d。U+0000\xe2\x80\x93U+D7FF 和 U+E000\xe2\x80\x93U+FFFF 范围内的字符由相同数字的代码单元表示,因此一切正常。

\n\n

通过将每个字符表示为两个 UTF-16 代码单元,将 U+010000 到 U+10FFFF 范围内的不常用的其他字符压缩到剩余空间 0xD800\xe2\x80\x930xDFFF 中,因此相当于Python 字符串"\\U00010000"是 C# "\\uD800\\uDC00"

\n\n
\n

为什么?

\n
\n\n

造成这种疯狂的原因是Windows NT系列本身使用UTF-16LE作为本机字符串编码,因此为了互操作性方便.NET选择了相同的编码。WinNT 选择了当时被认为是 UCS-2 的编码 \xe2\x80\x94,并且没有任何讨厌的代理代码单元对 \xe2\x80\x94,因为早期 Unicode 只具有 U+FFFF 之前的字符,而我想这将是每个人所需要的一切。

\n\n
\n

怎么修?

\n
\n\n

确实没有什么好的解决办法。其他一些不幸地将其字符串类型基于 UTF-16 代码单元的语言(Java、JavaScript)开始向其字符串添加方法,以便对它们进行一次计算一个代码点的操作;但目前.NET中还没有这样的功能。

\n\n

通常,您实际上并不需要始终需要使用正确的代码点项和索引来计数/查找/分割/排序/等字符串。但当你真的在 .NET 中这样做时,你的日子就会很糟糕。您最终必须重新实现每个通常很简单的方法,方法是手动遍历每个方法char并检查它是否是两个字符代理对的一部分,或者将字符串转换为代码点整数数组并返回。不管怎样,这都没什么乐趣。

\n\n

一个更优雅、更实用的选择是发明一台时间机器,这样我们就可以将 UTF-8 设计带回到 1988 年,并阻止 UTF-16 的存在。

\n