C# 比较utf-8编码字符串的两个字节[]是否与比较两个unicode字符串相同?

C# 比较utf-8编码字符串的两个字节[]是否与比较两个unicode字符串相同?,c#,unicode,C#,Unicode,我在关于utf-8的维基百科文章中发现了这一点: 将UTF-8字符串排序为无符号字节数组将产生与基于Unicode码点排序相同的结果 这使我相信,出于比较目的(排序、二进制搜索等),比较utf-8编码字符串的两个字节数组(即逐字节的memcmp)将得到与比较实际unicode字符串相同的结果 这是真的吗?不,不是 例如,À可以写成单个代码点(U+00C0带GRAVE的拉丁文大写字母A)或两个代码点(U+0041拉丁文大写字母AU+0300组合GRAVE重音) 这两种表示法的比较应该相等,但将有

我在关于utf-8的维基百科文章中发现了这一点:

将UTF-8字符串排序为无符号字节数组将产生与基于Unicode码点排序相同的结果

这使我相信,出于比较目的(排序、二进制搜索等),比较utf-8编码字符串的两个字节数组(即逐字节的memcmp)将得到与比较实际unicode字符串相同的结果

这是真的吗?不,不是

例如,
可以写成单个代码点(
U+00C0
带GRAVE的拉丁文大写字母A)或两个代码点(
U+0041
拉丁文大写字母A
U+0300
组合GRAVE重音)


这两种表示法的比较应该相等,但将有不同的字节编码。

是的,因为UTF-8编码中的序列字节和Unicode代码点之间存在一对一的映射

但是,除了查看原始代码点之外,还有比较Unicode字符串的方法。如果只将代码点(或UTF-8字节)视为数字,则会忽略特定于区域性的比较逻辑


要在.NET上正确实现特定区域性的比较和排序,应使用标准字符串比较函数。

这取决于“比较实际Unicode字符串”的含义

如果只是比较代码点(作为32位数字)而不是UTF-8编码的代码点,那么答案是肯定的:这将给出相同的结果。从代码点到UTF-8编码字节的映射是一对一的

如果要进行正确的Unicode字符串比较,而不是UTF-8的字节比较,答案是否定的。在Unicode中,可以有不同的方式来表示相同的字符。例如,é可以(至少)以两种方式表示:

  • U+00e9(带锐音符的拉丁文小写字母E)
    ,或
  • U+0065(拉丁文小写字母E)
    后接
    U+0301(组合锐重音)

一个正确编写的Unicode比较函数将认为这两个函数是相同的。

我在关于utf-8的维基百科文章中发现了这一点:

将UTF-8字符串排序为无符号字节数组将产生与基于Unicode码点排序相同的结果

这使我相信,出于比较目的(排序、二进制搜索等),比较utf-8编码字符串的两个字节数组(即逐字节的memcmp)将得到与比较实际unicode字符串相同的结果

这完全取决于“实际Unicode字符串”和“比较”的含义。在.Net Framework中,字符串采用Unicode的UTF-16格式。与UTF-8和UTF-32(引号中引用的代码点版本)字符串之间的相同比较相比,UTF-16字符串之间的简单二进制比较将产生不同的排序顺序


但对这些东西进行二元比较并不是很有用。您应该使用内置的文化感知比较。这是因为,出于所有目的,可以从不同的代码点序列构造相同的两个字符串。内置的比较会考虑这些因素。

它与代码点比较的代码点相同,也就是说,它不注意大小写折叠、文化顺序、组合或Unicode值以外的任何内容

当将字符串视为人类可读的文本时,这是非常无用的,但有时您只是希望能够将字符串放入排序中,因为某些算法(如您所说的二进制搜索)需要一致的排序,但一致排序的细节并不重要

但是需要注意的是,.NET提供的字符串顺序比较在内部使用的UTF-16上起作用,不维护代码点顺序。如果我们比较一个字符串(仅包含字符U+FF61)和一个字符串(仅包含字符U+10002),则.NET将后者存储为0xD800和0XDC02的代理项对

因此:

string.CompareOrdinal("\U0000ff61", "\U00010002");

两个返回值都大于零,即使前者的代码点值低于后者(我使用了\U形式而不是\U形式来更清楚地说明这一点)


如果“实际的unicode字符串”指的是.NET UTF-16字符串,那么您的问题的答案是否定的,原因与您认为它可能会起作用的原因相反。

这两个代码点肯定会编码为不同的UTF-8字节序列吗?或者你是说你不能往返Unicode->UTF-8->Unicode?不,他是说Unicode NFC中的一个字符串和NFD中的同一个字符串(以可能的规范化状态为例)和未规范化的同一个字符串不会有相同的代码点序列。另外,谈论UTF-8和Unicode之间的往返是没有意义的,因为UTF-8是Unicode,只是以特定的字节顺序存储。你知道Unicode比较函数的实际行为方式吗?有趣的是,我会得到一个UTF-8排序,这将是一个代码点排序,与.NET中的区域性不变顺序比较产生的结果不同,这仅仅是因为.NET使用UTF-16,它不提供代码点顺序。这很微妙,肯定会让一些可怜的程序员过早地变得灰暗:请注意,区域性不变量与序号不同。不变的文化几乎是一种虚构的文化(它看起来更像美国和英联邦之间共享的“中大西洋”盎格鲁文化,而不像任何其他文化),当你需要强制一致的行为而不是文化上正确的处理时,它是有用的。顺序比较严格适用于需要任意排序的情况(这在文化上是垃圾,但它的fa
string.Compare("\U0000ff61", "\U00010002", StringComparison.Ordinal);