InvariantCulture和序数字符串比较之间的区别


548

在c#中比较两个字符串是否相等时,InvariantCulture和Ordinal比较之间有什么区别?



2
对于那些使用的用户String1.Equals(String2, StringComparison.Ordinal),最好使用String1 == String2本质上String1.Equals(String2)是的,并且默认情况下是顺序区分大小写的比较。
Ghasan 2014年

3
@Ghasan不知道这是否会使==“更好”,但它是a)较短,b)对其确切作用的明确程度较低,并且c)String1在不将比较抛出a的情况下可以为null NullReferenceException
尤金·别列索夫斯基

3
@Ghasan 在.NET Framework页面(msdn.microsoft.com/en-us/library/…中提供了有关使用字符串的 MSDN官方最佳实践建议,建议使用显式指定StringComparison类型的重载。在字符串比较的情况下,表示String.Equals
Ohad Schneider

3
@EugeneBeresovsky为避免这种情况,NullReferenceException您可以简单地使用static方法:String.Equals(string1, string2, StringComparison.Ordinal)
Ohad Schneider

Answers:


302

不变文化

使用一组“标准”字符顺序(a,b,c等)。这与某些特定的语言环境相反,后者可能以不同的顺序对字符进行排序(“ a-with-acute”可能在“ a” 之前之后,具体取决于语言环境,依此类推)。

序数

另一方面,仅查看代表字符的原始字节的值。


http://msdn.microsoft.com/zh-cn/library/e6883c06.aspx上有一个很好的示例,它显示了各种StringComparison值的结果。一直到最后,它显示(摘录):

StringComparison.InvariantCulture:
LATIN SMALL LETTER I (U+0069) is less than LATIN SMALL LETTER DOTLESS I (U+0131)
LATIN SMALL LETTER I (U+0069) is less than LATIN CAPITAL LETTER I (U+0049)
LATIN SMALL LETTER DOTLESS I (U+0131) is greater than LATIN CAPITAL LETTER I (U+0049)

StringComparison.Ordinal:
LATIN SMALL LETTER I (U+0069) is less than LATIN SMALL LETTER DOTLESS I (U+0131)
LATIN SMALL LETTER I (U+0069) is greater than LATIN CAPITAL LETTER I (U+0049)
LATIN SMALL LETTER DOTLESS I (U+0131) is greater than LATIN CAPITAL LETTER I (U+0049)

您会看到InvariantCulture产生(U + 0069,U + 0049,U + 00131),序数产生(U + 0049,U + 0069,U + 00131)。


25
顺序比较着眼于代码点,而不是字节。
乔伊(Joey)2012年

143
我觉得这是有用的信息,但实际上并不能回答问题。确定两个字符串的相等性时,是否有任何理由使用InvarintCulture而不是Ordinal?似乎InvariantCulture将用于对字符串进行排序,而Ordinal应该用于进行相等性检查(我们不在乎重音符号-a出现在a之前或之后,只是有所不同)。不过,我自己对此一点不确定。
MPavlak 2012年

18
请参阅msdn.microsoft.com/zh-cn/library/ms230117%28v=vs.90%29.aspx,请注意,建议使用字符串规范化和序数比较。
MPavlak 2012年

23
Ordinal快得多
Darren 2014年

9
C#字符串比较测试中已发布了良好的性能测试结果,该结果说明了每种不同的字符串比较方法的性能及其时间。
Kumar C

259

例如,这很重要-有一种叫做字符扩展的东西

var s1 = "Strasse";
var s2 = "Straße";

s1.Equals(s2, StringComparison.Ordinal);           //false
s1.Equals(s2, StringComparison.InvariantCulture);  //true

使用InvariantCultureß字符可扩展为ss。


1
Ordinal和之间的区别InvariantCulture吗?这就是最初的问题。
Matthijs Wessels 2014年

3
对于那些不知道的人ß,应该指出的是ß,至少德语等于一个double s,来源:en.wikipedia.org/wiki/%C3%9F
彼得

19
这并不完全正确@Peter,你不能使用ßss交替德语(我的母语)。在某些情况下,这两种方法都是合法的(但通常都过时了/不建议使用),并且在某些情况下,只允许使用一种形式。
enzi

5
这个简单的例子清楚地说明了这两个比较之间的区别。我想我现在明白了。
BrianLegg

4
不得不尝试:ideone.com/j8Dv做得真酷!还有一点德语课。现在想知道ß和ss有什么区别...
Mzn

111

指向在.NET Framework中使用字符串的最佳做法

  • 使用StringComparison.OrdinalStringComparison.OrdinalIgnoreCase比较作为与区域性无关的字符串匹配的安全默认设置。
  • 使用比较StringComparison.OrdinalStringComparison.OrdinalIgnoreCase以获得更好的性能。
  • 根据比较何时在语言上不相关(例如符号),使用非语言StringComparison.OrdinalStringComparison.OrdinalIgnoreCase值代替字符串操作CultureInfo.InvariantCulture

最后:

  • StringComparison.InvariantCulture在大多数情况下,请勿使用基于的字符串操作。少数例外之一是当您保留具有语言意义但与文化无关的数据时。

56

另一个方便的区别(英语中重音不常见)是InvariantCulture比较会先按不区分大小写的方式比较整个字符串,然后如果有必要(并要求)在仅对不同字母进行比较后再按大小写进行区分。(当然,您也可以进行不区分大小写的比较,这不能区分大小写。)已 更正:重音字母被认为是相同字母的另一种形式,并且比较字符串时首先忽略重音,如果通用字母都匹配(考虑不同的大小写,除了在不区分大小写的比较中最终未忽略),然后对它们进行解释。这将其他单词的重音版本彼此靠近分组,而不是在第一个重音差异时完全分开。这是您通常在字典中会发现的排序顺序,大写单词出现在其小写字母对等的旁边,而带重音的字母则位于相应的无重音字母附近。

序数比较严格地比较数字字符值,从第一个差开始停止。这样排序的大写字母与小写字母完全分开(重音字母大概与小写字母分开),因此大写字母的排序方式与其小写字母相当。

InvariantCulture还认为大写字母要比小写字母大,而Ordinal认为大写字母要比小写字母小(以前的ASCII保留在计算机使用小写字母之前,大写字母被首先分配,因此其值比小写字母小)。稍后添加)。

例如,按顺序: "0" < "9" < "A" < "Ab" < "Z" < "a" < "aB" < "ab" < "z" < "Á" < "Áb" < "á" < "áb"

通过InvariantCulture: "0" < "9" < "a" < "A" < "á" < "Á" < "ab" < "aB" < "Ab" < "áb" < "Áb" < "z" < "Z"


我对此进行了另一番研究,发现InvariantCulture示例与我对重音符号处理的解释之间存在不一致之处。该示例似乎是正确的,因此我已将解释更正为一致的。InvariantCulture比较不止于第一个不同的重音符号,并且似乎仅在其余字符串除重音和大小写匹配的情况下才考虑同一字母的重音差异。然后,在较早的区分大小写之前要考虑重音差异,因此“ Aaba” <“aába”。
罗伯·帕克

31

尽管问题是关于相等性的,但为了便于快速参考,此处使用一些文化对一些字符串的顺序进行排序,以说明其中的某些特质。

Ordinal          0 9 A Ab a aB aa ab ss Ä Äb ß ä äb      
IgnoreCase       0 9 a A aa ab Ab aB ss ä Ä äb Äb ß      
--------------------------------------------------------------------
InvariantCulture 0 9 a A  ä Ä aa ab aB Ab äb Äb ss ß     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ß ss     
--------------------------------------------------------------------
da-DK            0 9 a A  ab aB Ab ss ß ä Ä äb Äb aa     
IgnoreCase       0 9 A a  Ab aB ab ß ss Ä ä Äb äb aa     
--------------------------------------------------------------------
de-DE            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     
--------------------------------------------------------------------
en-US            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     
--------------------------------------------------------------------
ja-JP            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     

观察结果:

  • de-DEja-JPen-US以相同方式排序
  • Invariantssß上述三种文化有所不同
  • da-DK 排序完全不同
  • IgnoreCase所有抽样文化标志事项

用于生成上表的代码:

var l = new List<string>
    { "0", "9", "A", "Ab", "a", "aB", "aa", "ab", "ss", "ß",
      "Ä", "Äb", "ä", "äb", "あ", "ぁ", "ア", "ァ", "A", "亜" };

foreach (var comparer in new[]
{
    StringComparer.Ordinal,
    StringComparer.OrdinalIgnoreCase,
    StringComparer.InvariantCulture,
    StringComparer.InvariantCultureIgnoreCase,
    StringComparer.Create(new CultureInfo("da-DK"), false),
    StringComparer.Create(new CultureInfo("da-DK"), true),
    StringComparer.Create(new CultureInfo("de-DE"), false),
    StringComparer.Create(new CultureInfo("de-DE"), true),
    StringComparer.Create(new CultureInfo("en-US"), false),
    StringComparer.Create(new CultureInfo("en-US"), true),
    StringComparer.Create(new CultureInfo("ja-JP"), false),
    StringComparer.Create(new CultureInfo("ja-JP"), true),
})
{
    l.Sort(comparer);
    Console.WriteLine(string.Join(" ", l));
}

1
嗯-好的,很高兴您进行了这项研究,并发表了您的发现,尽管我不确定您的意思是什么。无论如何,丹麦语可能不是“最重要的文化”之一(尽管实际上有500万丹麦人很喜欢他们的文化),但是如果您将“ aa”作为附加测试字符串,而将“ da-DK”作为其他测试文化,您会看到一些有趣的结果。
RenniePet 2014年

1
@RenniePet谢谢你。我添加了丹麦语,因为其排序方式与所使用的其他三种文化截然不同。(由于表示反讽的表情符号似乎不像我想象的那样容易理解,所以我删除了“最重要的文化”注释。毕竟,BCL并没有CultureComparer我们可以使用的(对于此表,Danish文化(信息)非常重要。)
Eugene Beresovsky 2014年

1
谢谢。我的确意识到,您的“最重要的文化”评论原本应该带有一小撮盐的意思-只是我年龄太大了,无法使用表情符号。我认为发短信变得如此普遍,以至于使用表情符号就像在讲完笑话后就解释自己的笑话,无论是否有人在笑。顺便提一下,其他北欧文化(芬兰,挪威和瑞典)与丹麦相同,只是对“ aa”的特殊处理-这当然证明丹麦是上等文化。
RenniePet 2014年

1
就其价值而言,丹麦文对ä和a的排序方式有所不同,因为特殊字母æ(ae),ø(oe,ö)和å(aa,ä)的位置按书面顺序排列。
Alrekr


5

这是一个示例,其中使用InvariantCultureIgnoreCase和OrdinalIgnoreCase进行的字符串相等比较不会给出相同的结果:

string str = "\xC4"; //A with umlaut, Ä
string A = str.Normalize(NormalizationForm.FormC);
//Length is 1, this will contain the single A with umlaut character (Ä)
string B = str.Normalize(NormalizationForm.FormD);
//Length is 2, this will contain an uppercase A followed by an umlaut combining character
bool equals1 = A.Equals(B, StringComparison.OrdinalIgnoreCase);
bool equals2 = A.Equals(B, StringComparison.InvariantCultureIgnoreCase);

如果运行此命令,则equals1将为false,equals2将为true。


只是添加另一个类似的示例,但带有字符串文字,如果a="\x00e9"(e)和b="\x0065\x0301"(e与重音符组合),StringComparer.Ordinal.Equals(a, b)将返回false,而StringComparer.InvariantCulture.Equals(a, b)将返回true。
乔治·希拉尔

2

无需使用花哨的Unicode字符示例来显示差异。这是我今天发现的一个简单示例,令人惊讶,它仅包含ASCII字符。

根据ASCII表,0通常进行比较时(0x48)小于_(0x95)。InvariantCulture则相反(下面的PowerShell代码):

PS> [System.StringComparer]::Ordinal.Compare("_", "0")
47
PS> [System.StringComparer]::InvariantCulture.Compare("_", "0")
-1

-7

始终尝试在将其视为重载的字符串方法中使用InvariantCulture。通过使用InvariantCulture,您可以放心。许多.NET程序员可能不会使用此功能,但是如果您的软件将用于不同的地区,则InvariantCulture是一项非常方便的功能。


3
如果您的软件不会被其他地区使用,则它比Ordinal慢得多。
凯尔

4
我之所以考虑降低投票率,是因为您当然不会通过偶然的反应来思考。尽管其中蕴含着真理。如果您的应用程序已广泛传播于多种文化中……那当然不能保证您的开场白“总是尝试使用InvariantCulture”,对吗?令您惊讶的是,这些年来您没有再来编辑这种疯狂的想法,他们收到了低估,也许还有更多的经验。
Suamere
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.