Разница между InvariantCulture и порядком сравнения строк

548

При сравнении двух строк в c # на равенство, в чем разница между InvariantCulture и Порядковым сравнением?

Капил
источник
Может быть, siao2.com/2004/12/29/344136.aspx ? (гуглил)
Сыр Даниш
2
Для тех String1.Equals(String2, StringComparison.Ordinal), кто использует , вам лучше использовать, String1 == String2что по сути, String1.Equals(String2)и это по умолчанию порядковое сравнение с учетом регистра.
Гасан
3
@Ghasan Не уверен, что это делает ==«лучше», но это а) короче, б) менее ясно о том, что именно он делает, и в) String1может быть нулевым без сравнения, бросая a NullReferenceException.
Евгений Бересовский,
3
@ Ghasan, официальный MSDN Best Practices по использованию строк на странице .NET Framework ( msdn.microsoft.com/en-us/library/… ), рекомендует использовать перегрузки, которые явно указывают StringComparisonтип. В случае сравнения строк это означает String.Equals.
Охад Шнайдер
3
@EugeneBeresovsky Во избежание NullReferenceExceptionвы можете просто использовать статический метод: String.Equals(string1, string2, StringComparison.Ordinal).
Охад Шнайдер

Ответы:

302

InvariantCulture

Использует «стандартный» набор порядков символов (a, b, c, ... и т. Д.). Это отличается от некоторых конкретных локалей, которые могут сортировать символы в разных порядках («с острым» может быть до или после «а», в зависимости от локали и т. Д.).

порядковый

С другой стороны, смотрит чисто на значения необработанных байтов, которые представляют символ.


На http://msdn.microsoft.com/en-us/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).

JaredReisinger
источник
25
Порядковое сравнение смотрит на кодовые точки , а не байты.
Джои
144
Я чувствую, что это полезная информация, но на самом деле не отвечает на вопрос. При определении равенства двух строк, есть ли причина использовать InvarintCulture вместо Ordinal? Кажется, что InvariantCulture будет использоваться для сортировки строк, а Ordinal должен использоваться для проверки на равенство (нас не волнует, что ударение-a идет до или после a, оно просто другое). Хотя я сам немного не уверен в этом.
MPavlak
18
См. Msdn.microsoft.com/en-us/library/ms230117%28v=vs.90%29.aspx и обратите внимание, что рекомендуется нормализация строк и порядковое сравнение.
MPavlak
23
Порядковый гораздо быстрее
Даррен
9
Имеются хорошие результаты тестов производительности, опубликованные C # String Comparision Tests, в которых сообщается о производительности различных методов сравнения строк и их времени.
Кумар C
262

Это имеет значение, например, есть такая вещь, как расширение персонажа

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
3
Для тех, кто не знает, ßследует отметить, что, ßпо крайней мере, по-немецки равно двойной s, Источник: en.wikipedia.org/wiki/%C3%9F
Питер
20
Это не совсем правильно @Peter, вы не можете использовать ßи ssвзаимозаменяемо на немецком языке (я носитель языка). Есть случаи, когда оба являются законными (но часто один устарел / не рекомендуется), и есть случаи, когда разрешена только одна форма.
Enzi
5
Этот простой пример ясно демонстрирует разницу между двумя сравнениями. Я думаю, что я получаю это сейчас.
BrianLegg
4
Пришлось попробовать: ideone.com/j8Dv так круто! Небольшой урок и по немецкому. Интересно, в чем разница между ß и ss сейчас ...
Mzn
111

Указание на рекомендации по использованию строк в .NET Framework :

  • Используйте StringComparison.OrdinalилиStringComparison.OrdinalIgnoreCase для сравнения в качестве безопасного значения по умолчанию для сопоставления строк, не зависящего от культуры.
  • Используйте сравнения с StringComparison.OrdinalилиStringComparison.OrdinalIgnoreCase для лучшей производительности.
  • Используйте неязыковые StringComparison.Ordinalили StringComparison.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 не останавливается на первом различающемся акценте и, по-видимому, учитывает разницу в акценте только для той же буквы, если остальные строки совпадают, кроме ударений и регистра. Разница в акценте учитывается перед разницей в более раннем случае, поэтому «Ааба» <«ааба».
Роб Паркер
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-DE, ja-JPИ en-USвроде так же , как
  • Invariantтолько виды ssи в ßотличие от вышеупомянутых трех культур
  • 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
Хммм - хорошо, хорошо, что вы провели это исследование и опубликовали свои выводы, хотя я не совсем уверен, в чем ваша точка зрения. В любом случае, датский язык может быть не одной из «самых важных культур» (хотя 5 миллионов датчан действительно очень любят их культуру), но если вы добавите «aa» в качестве дополнительной тестовой строки, а «da-DK» в качестве дополнительная культура тестирования, вы увидите некоторые интересные результаты.
RenniePet
1
@RenniePet Спасибо за это. Я добавил датский, поскольку он сортируется совсем не так, как 3 другие культуры. (Так как смайлики, указывающие на иронию, кажутся не столь хорошо понятными в сети чтения английского языка, как я бы предположил, я удалил комментарий «самых важных культур». В конце концов, BCL не имеет функции, CultureComparerкоторую мы могли бы использовать проверить. Для этой таблицы Danishкультура (инфо) оказалась очень важной.)
Евгений Бересовский
1
Спасибо. Я осознал, что ваш комментарий о «наиболее важных культурах» должен был быть взят с крошкой соли - просто я слишком стар, чтобы использовать смайлики. Я полагаю, что текстовые сообщения стали настолько распространенными, что использование смайликов - это что-то вроде объяснения ваших шуток после того, как вы им рассказываете, независимо от того, смеется кто-то или нет. Между прочим, другие скандинавские культуры (финская, норвежская и шведская) такие же, как и датские, за исключением особого обращения с «аа», которое доказывает, что датский язык является высшей культурой, конечно.
RenniePet
1
Для чего это стоит, датский сортирует ä и aa по-разному из-за расположения специальных букв æ (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.

Dwedit
источник
Просто чтобы добавить другой подобный пример, но со строковыми литералами, если 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", не так ли? Я удивлен, что вы не возвращались годами, чтобы отредактировать это сумасшествие после получения отрицательного голоса и, возможно, большего опыта.
Суамер