# InvariantCulture vs. Ordinal string comparison: what’s the difference?

## Problem

What is the difference between InvariantCulture and Ordinal comparison when comparing two strings in C# for equality?

## Solution #1

Uses a set of “standard” character orderings (a,b,c, … etc.). This is in contrast to some localities, which may sort characters in different orders (for example, depending on the locale, ‘a-with-acute’ may come before or after ‘a’, and so on).

On the other hand, only considers the raw byte(s) values that represent the character.

http://msdn.microsoft.com/en-us/library/e6883c06.aspx has a fantastic example. that displays the results of different StringComparison values It displays (excerpted) all the way to the end:

``````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)
``````

Ordinal gives (U+0049, U+0069, U+00131), 069, U+0049, U+0049, U+0049, U+0049, U+0049, U+0049, U+0049, U+0049, U+0049, U+0049,

## Solution #2

It matters, for example, because there is a concept known as character expansion.

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

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

The ß letter is enlarged to ss with InvariantCulture.

## Solution #3

Best Practices for Using Strings in the.NET Framework are as follows:

And finally:

## Solution #4

Another useful distinction (in English, where accents are uncommon) is that an InvariantCulture comparison compares the full strings case-insensitively first, then distinguishes by case if necessary (and requested) after comparing solely on the distinct letters. (Of course, you may execute a case-insensitive comparison, which will not discriminate between cases.) Corrected: Accented letters are treated as a different flavor of the same letters, and the string is tested without accents first, then with accents if the general letters all match (much as with differing case except not ultimately ignored in a case-insensitive compare). This clusters accented versions of the same word near each other rather than separating them at the first accent difference. This is the order in which you should arrange your data.

An ordinal comparison compares numeric character values only and stops at the first discrepancy. This separates capitalized letters from lowercase letters (and likely accented letters from those), so uppercase words would sort nowhere near their lowercase counterparts.

Ordinal believes capitals to be less than lowercase, but InvariantCulture considers capitals to be more than lowercase (a holdover of ASCII from the old days before computers had lowercase letters, the uppercase letters were allocated first and thus had lower values than the lowercase letters added later).

Ordinal, for example: “0” “9” “A” “Ab” “Z” “a” “aB” “ab” “z” “” “b” “á” “áb”

“0” “9” “a” “A” “á” “” “ab” “aB” “Ab” “áb” “b” “z” “Z” “0” “9” “a” “A” “á” “” “ab” “aB” “Ab” “áb” “b” “z” “Z”

## Solution #5

Although the question is about equality, for quick visual reference, here the order of some strings sorted using a couple of cultures illustrating some of the idiosyncrasies out there.

``````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 ß ァ ぁ ア あ 亜
``````

Observations:

The following is the code that was used to create the aforementioned table:

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

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));
}
``````