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

时间:2017-09-01 09:51:12

标签: c# unicode encoding utf-8

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

假设那些"事实"是对的:

  1. Unicode是"协议"它定义了每个角色。
  2. UTF-8定义"实现" - 如何存储这些角色。
  3. Unicode定义字符范围从0x0000到0x10FFFF(source
  4. 根据C# reference,每个字符的可接受范围是0x0000到0xFFFF。我不明白其他字符是什么,高于0xFFFF,并在Unicode协议中定义?

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

    u"\U00010000"  #WORKING!!!
    

    不适用于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
    

    为什么呢?怎么修?

2 个答案:

答案 0 :(得分:1)

  

根据C#引用,每个char的可接受范围是0x0000到0xFFFF。我不明白其他字符是什么,高于0xFFFF,并在Unicode协议中定义?

不幸的是,C#/。NET char不代表Unicode字符。

char是0x0000到0xFFFF范围内的16位值,表示一个“UTF-16代码单元”。 U + 0000-U + D7FF和U + E000-U + FFFF范围内的字符由相同数字的代码单元表示,因此一切都很好。

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

  

为什么?

这种疯狂的原因是Windows NT系列本身使用UTF-16LE作为本机字符串编码,因此为了便于互操作,.NET选择了相同的。 WinNT选择了那种编码 - 当时被认为是UCS-2并且没有任何讨厌的代理代码单元对 - 因为在早期Unicode只有U + FFFF的字符,而且这种想法将是全部任何人都需要。

  

如何解决?

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

通常,您 但是当你真的这么做的时候,在.NET中,你会陷入困境。您最终必须通过手动遍历每个char来重新实现每个常规方法,并检查它是否为双字符代理项对的一部分,或者将字符串转换为代码点整数和返回数组。无论哪种方式,这都不是很有趣。

更优雅,更实用的选择是发明一台时间机器,所以我们可以将UTF-8设计发回1988年,防止UTF-16存在。

答案 1 :(得分:0)

Unicode有所谓的飞机wiki)。

如您所见,C#的char类型仅支持第一个平面,平面0,基本多语言平面

我知道C#使用UTF-16编码的事实,所以我有点惊讶地发现它不支持char数据类型中第一个平面之外的代码点。 (避免自己遇到这个问题......)。

这是char实施中的人为限制,但这是可以理解的。 .NET的设计者可能并不想将自己的字符数据类型的抽象与Unicode定义的抽象联系起来,以防标准无法生存(它已经取代了其他标准)。这当然是我的猜测。它只是"使用" UTF-16用于存储器表示。

UTF-16使用技巧将高于0xFFFF的代码点压缩为16位,因为您可以阅读here。从技术上讲,这些代码点由2"字符"组成,即所谓的代理。从这个意义上讲,它打破了一个代码点=一个字符"抽象。

你可以通过使用stringchar数组来解决这个问题。如果您有更多具体问题,可以在StackOverflow和其他地方找到有关使用.NET中所有Unicode代码点的大量信息。