Різниця між порівнянням InvariantCulture та Ordinal string


548

Якщо порівнювати два рядки в c # для рівності, чим відрізняється InvariantCulture від звичайного порівняння?


Може бути, siao2.com/2004/12/29/344136.aspx ? (
гугл

2
Для тих, хто використовує String1.Equals(String2, StringComparison.Ordinal), ви краще використовувати String1 == String2властивість, яка є внутрішньою, String1.Equals(String2)і це за замовчуванням звичайне порівняння з урахуванням регістру.
Ghasan

3
@Ghasan Не впевнений, чи це робить =="кращим", але це а) коротше, б) менш чітко про те, що саме це робить, і в) String1може бути нульовим без порівняння, що кидає а NullReferenceException.
Євген Бересовський

3
@Ghasan офіційна найкраща практика використання MSDN на використанні рядків на сторінці .NET Framework ( msdn.microsoft.com/en-us/library/… ) рекомендує використовувати перевантаження, які чітко визначають StringComparisonтип. У разі порівняння рядків це означає String.Equals.
Охад Шнайдер

3
@EugeneBeresovsky Щоб уникнути NullReferenceExceptionви можете просто використовувати статичний метод: String.Equals(string1, string2, StringComparison.Ordinal).
Охад Шнайдер

Відповіді:


302

Інваріантна культура

Використовує "стандартний" набір порядків символів (a, b, c, ... тощо). Це на відміну від деяких конкретних локалів, які можуть сортувати символи в різних порядках ("a-with-shar" може бути до або після "a", залежно від мови тощо).

Звичайні

З іншого боку, суто дивиться на значення необроблених байтів, що представляють символ.


На сайті http://msdn.microsoft.com/en-us/library/e6883c06.aspx є чудовий зразок, який показує результати різних значень StringComppare. У кінцевому підсумку він показує (уривок):

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)

Ви можете бачити, що інваріантна культура дає (U + 0069, U + 0049, U + 00131), звичайні врожаї (U + 0049, U + 0069, U + 00131).


25
Звичайне порівняння розглядає кодові точки , а не байти.
Джой

143
Я відчуваю, що це корисна інформація, але насправді не відповідає на питання. Визначаючи рівність двох рядків, чи є причина використовувати InvarintCulture замість Ordinal? Здається, InvariantCulture буде використовуватися для сортування рядків, а Ordinal слід використовувати для перевірки рівності (нас не хвилює, що наголос - a до або після, це просто інше). Хоча я сам трохи не впевнений у цьому.
MPavlak

18
Див. Msdn.microsoft.com/en-us/library/ms230117%28v=vs.90%29.aspx і зауважимо, що рекомендується нормалізація рядків та порядкове порівняння.
MPavlak

23
Звичайний набагато швидший
Даррен

9
Опубліковані хороші результати тесту на ефективність, опубліковані тести C # String Comparision Tests, які розповідають про ефективність різних методів порівняння рядків та їх час.
Кумар C

261

Має значення, наприклад - є річ, яка називається розширення символів

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/j8DvDo так круто! Невеликий урок німецької мови. Цікаво, яка зараз різниця між ß та 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 за старих часів, перш ніж на комп’ютерах були малі літери, великі літери були виділені першими і, таким чином, мали менші значення, ніж малі літери додано пізніше).

Наприклад, Ordinal: "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-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 мільйонів датчан насправді дуже люблять свою культуру), але якщо ви кинете "аа" в якості додаткової тестової рядки, а "да-ДК" в якості Додаткова культура тестування, ви побачите кілька цікавих результатів.
RenniePet

1
@ RenniePet Дякую за це. Я додав датську мову, оскільки вона сортується зовсім інакше, ніж 3 інші культури, що використовуються. (Оскільки смайлики, що вказують на іронію, не дуже добре розуміються в мережі читання англійською мовою, як я вважав, я видалив коментар "найважливіших культур". Зрештою, BCL не містить функції, CultureComparerяку ми могли б використати щоб перевірити. Для цієї таблиці Danishкультура (інформація) виявилася дуже важливою.)
Євген Бересовський

1
Дякую. Я зрозумів, що коментар до ваших "найважливіших культур" мав бути зроблений із зерном солі - я просто занадто старий, щоб використовувати смайлики. Я вважаю, що текстові повідомлення стали настільки поширеними, що використання смайликів начебто пояснює ваші жарти після того, як ви їх розкажете, незалежно від того, чи хтось сміється чи ні. До речі, інші скандинавські культури (фінська, норвезька та шведська) такі самі, як датські, за винятком дуже особливого поводження з "аа" - що, безумовно, доводить, що датська мова, безумовно, є вищою культурою.
RenniePet

1
Що даремно, датські сортують ä і aa по-різному через розташування спеціальних літер æ (ae), ø (oe, ö) та å (aa, ä) в кінці алфавіту в письмовому порядку.
Альрекр


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

Якщо це запустити, значення рівняння1 буде помилковим, а значення рівняння2 - істинним.


Просто додамо ще один подібний приклад, але з рядковими літералами, якщо a="\x00e9"(e гострий) та b="\x0065\x0301"(e поєднаний з гострим наголосом), StringComparer.Ordinal.Equals(a, b)повернеться хибним, а StringComparer.InvariantCulture.Equals(a, b)повернеться істинним.
Джордж Геляр

2

Не потрібно використовувати фантазійні приклади Unicode char, щоб показати різницю. Ось один простий приклад, який я дізнався сьогодні, що дивно, що складається лише з символів 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
Якщо ваше програмне забезпечення не використовуватимуться різними культурами, воно набагато повільніше, ніж звичайне.
Кайл

4
Я вважав заборону, тому що ви, звичайно, не продумали свою випадкову відповідь. Хоча всередині цього є зерно істини. ЯКЩО ваша заявка широко розповсюджена серед декількох культур ... Це, безумовно, не гарантує ваших вступних слів "Завжди намагайся використовувати InvariantCulture", чи не так? Я здивований, що ти не повернувся впродовж багатьох років, щоб відредагувати це божевілля після отримання подзвону та, можливо, більше досвіду.
Суамер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.