Différence entre InvariantCulture et comparaison de chaînes ordinales

548

Lorsque vous comparez deux chaînes en c # pour l'égalité, quelle est la différence entre InvariantCulture et la comparaison ordinale?

Kapil
la source
Peut-être siao2.com/2004/12/29/344136.aspx ? (googlé)
Cheese Daneish
2
Pour ceux qui utilisent String1.Equals(String2, StringComparison.Ordinal), il vaut mieux utiliser String1 == String2ce qui est intrinsèquement String1.Equals(String2)et c'est par défaut une comparaison ordinale sensible à la casse.
Ghasan
3
@Ghasan Je ne sais pas si cela rend =="mieux", mais c'est a) plus court, b) moins explicite sur ce qu'il fait exactement et c) String1peut être nul sans que la comparaison ne lance a NullReferenceException.
Eugene Beresovsky
3
@Ghasan les meilleures pratiques MSDN officielles pour l'utilisation de chaînes dans la page .NET Framework ( msdn.microsoft.com/en-us/library/… ) recommande l'utilisation de surcharges qui spécifient explicitement le StringComparisontype. Dans le cas de la comparaison de chaînes, cela signifie String.Equals.
Ohad Schneider du
3
Pour éviter @EugeneBeresovsky NullReferenceExceptionvous pouvez simplement utiliser la méthode statique: String.Equals(string1, string2, StringComparison.Ordinal).
Ohad Schneider

Réponses:

302

InvariantCulture

Utilise un ensemble "standard" d'ordonnances de caractères (a, b, c, ... etc.). Cela contraste avec certains paramètres régionaux spécifiques, qui peuvent trier les caractères dans des ordres différents («a-with-aigu» peut être avant ou après «a», selon les paramètres régionaux, etc.).

Ordinal

D'un autre côté, regarde uniquement les valeurs des octets bruts qui représentent le caractère.


Il existe un excellent exemple à http://msdn.microsoft.com/en-us/library/e6883c06.aspx qui montre les résultats des différentes valeurs StringComparison. À la fin, cela montre (extrait):

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)

Vous pouvez voir que lorsque InvariantCulture donne (U + 0069, U + 0049, U + 00131), les rendements ordinaux (U + 0049, U + 0069, U + 00131).

JaredReisinger
la source
25
La comparaison ordinale regarde les points de code , pas les octets.
Joey
144
Je pense que c'est une information utile, mais ne répond pas vraiment à la question. Lors de la détermination de l' égalité de deux chaînes, y a-t-il une raison d'utiliser InvarintCulture au lieu d'Ordinal? Il semble que InvariantCulture soit utilisé pour trier les chaînes, et Ordinal devrait être utilisé pour la vérification d' égalité (peu importe que l'accent-a vienne avant ou après a, c'est tout simplement différent). Cependant, je suis moi-même un peu incertain de ce point.
MPavlak
18
Voir msdn.microsoft.com/en-us/library/ms230117%28v=vs.90%29.aspx et notez que la normalisation des chaînes et la comparaison ordinale sont recommandées.
MPavlak
23
Ordinal est beaucoup plus rapide
Darren
9
Il y a de bons résultats de tests de performances publiés . Tests de comparaison de chaînes C # qui indiquent les performances de chaque méthode de comparaison de chaînes et leur temps.
Kumar C
262

Cela importe, par exemple - il y a une chose appelée expansion des personnages

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

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

Le InvariantCulturecaractère ß est étendu aux ss.

Ventsyslav Raikov
la source
1
Est-ce que cette chose diffère également d'une manière ou d'une autre entre Ordinalet InvariantCulture? C'est de cela qu'il s'agit à l'origine.
Matthijs Wessels
3
Pour ceux qui ne le savent pas, ßil convient de noter qu'au ßmoins en allemand équivaut à un double s, Source: en.wikipedia.org/wiki/%C3%9F
Peter
20
Ce n'est pas tout à fait correct @Peter, vous ne pouvez pas utiliser ßet de ssmanière interchangeable en allemand (je suis un locuteur natif). Il y a des cas où les deux sont légaux (mais souvent l'un est obsolète / déconseillé) et il y a des cas où un seul formulaire est autorisé.
enzi
5
Cet exemple simple montre clairement la différence entre les 2 comparaisons. Je pense que je comprends cela maintenant.
BrianLegg
4
J'ai dû l'essayer: ideone.com/j8DvDo so cool! Une petite leçon d'allemand aussi. Vous vous demandez quelle est la différence entre ß et ss maintenant ...
Mzn
111

Pointant vers les meilleures pratiques pour l'utilisation de chaînes dans le .NET Framework :

  • Utilisez StringComparison.Ordinalou StringComparison.OrdinalIgnoreCasepour les comparaisons comme valeur par défaut pour la correspondance de chaînes indépendante de la culture.
  • Utilisez des comparaisons avec StringComparison.Ordinalou StringComparison.OrdinalIgnoreCasepour de meilleures performances.
  • Utilisez les valeurs non linguistiques StringComparison.Ordinalou StringComparison.OrdinalIgnoreCaseau lieu d'opérations de chaîne en fonction du CultureInfo.InvariantCulturemoment où la comparaison n'est pas pertinente sur le plan linguistique (symbolique, par exemple).

Et enfin:

  • N'utilisez pas d'opérations de chaîne basées sur StringComparison.InvariantCulturedans la plupart des cas . L'une des rares exceptions concerne les données persistantes sur le plan linguistique mais culturellement agnostiques.
Dariusz
la source
56

Une autre différence pratique (en anglais où les accents sont rares) est qu'une comparaison InvariantCulture compare d'abord les chaînes entières en respectant la casse, puis si nécessaire (et demandé) distingue par cas après avoir d'abord comparé uniquement sur les lettres distinctes. (Vous pouvez également faire une comparaison insensible à la casse, bien sûr, qui ne distingue pas par cas.) Corrigé:Les lettres accentuées sont considérées comme une autre saveur des mêmes lettres et la chaîne est d'abord comparée en ignorant les accents, puis en les comptabilisant si les lettres générales correspondent toutes (tout comme avec une casse différente, mais pas finalement ignorée dans une comparaison insensible à la casse). Cela regroupe les versions accentuées du même mot autrement près les unes des autres au lieu d'être complètement séparées à la première différence d'accent. Il s'agit de l'ordre de tri que vous trouverez généralement dans un dictionnaire, avec des mots en majuscule apparaissant juste à côté de leurs équivalents en minuscules et des lettres accentuées proches de la lettre non accentuée correspondante.

Une comparaison ordinale compare strictement les valeurs des caractères numériques, s'arrêtant à la première différence. Cela trie les lettres majuscules complètement distinctes des lettres minuscules (et les lettres accentuées vraisemblablement distinctes de celles-ci), de sorte que les mots en majuscules ne seraient nulle part proches de leurs équivalents minuscules.

InvariantCulture considère également que les majuscules sont plus grandes que les minuscules, alors que Ordinal considère les majuscules moins que les minuscules (une trace d'ASCII de l'ancien temps avant que les ordinateurs aient des lettres minuscules, les lettres majuscules étaient allouées en premier et avaient donc des valeurs inférieures aux lettres minuscules). ajouté plus tard).

Par exemple, par Ordinal: "0" < "9" < "A" < "Ab" < "Z" < "a" < "aB" < "ab" < "z" < "Á" < "Áb" < "á" < "áb"

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

Rob Parker
la source
J'ai jeté un coup d'œil à cela et j'ai remarqué une incohérence entre l'exemple InvariantCulture et mon explication sur la gestion des caractères accentués. L'exemple semble être correct, j'ai donc corrigé l'explication pour qu'elle soit cohérente. La comparaison InvariantCulture ne s'arrête pas au premier accent différent et semble ne considérer une différence d'accent sur la même lettre que si le reste des chaînes correspondent en plus des accents et de la casse. Une différence d'accent est ensuite considérée avant une différence de cas antérieure, donc "Aaba" <"aába".
Rob Parker
31

Bien que la question porte sur l' égalité , pour une référence visuelle rapide, voici l'ordre de certaines chaînes triées à l' aide de deux cultures illustrant certaines des idiosyncrasies là-bas.

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:

  • de-DE, ja-JPEt en-USsorte de la même façon
  • Invarianttrie seulement sset ßdifféremment des trois cultures ci-dessus
  • da-DK trie très différemment
  • le IgnoreCasedrapeau est important pour toutes les cultures échantillonnées

Le code utilisé pour générer le tableau ci-dessus:

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));
}
Eugene Beresovsky
la source
1
Hmmm - OK, c'est bien que vous ayez fait cette recherche et publié vos résultats, bien que je ne sois pas vraiment sûr de votre point de vue. Quoi qu'il en soit, le danois n'est peut-être pas l'une des "cultures les plus importantes" (bien que 5 millions de Danois aiment en fait plutôt leur culture), mais si vous ajoutez "aa" comme chaîne de test supplémentaire et "da-DK" comme une culture de test supplémentaire, vous verrez des résultats intéressants.
RenniePet
1
@RenniePet Merci pour cela. J'ai ajouté le danois, car il est assez différent des 3 autres cultures utilisées. (Comme les émoticônes indiquant l'ironie ne semblent pas être aussi bien comprises dans le web de lecture en anglais que je l'aurais supposé, j'ai supprimé le commentaire "les cultures les plus importantes". Après tout, le BCL ne comporte pas un CultureComparerque nous pourrions utiliser Pour vérifier ce tableau, la Danishculture (info) s'est avérée très importante.)
Eugene Beresovsky
1
Merci. J'ai réalisé que votre commentaire sur "les cultures les plus importantes" était destiné à être pris avec un grain de sel - c'est juste que je suis devenu trop vieux pour utiliser des émoticônes. Je pense que l'envoi de SMS est devenu si courant que l'utilisation d'émoticônes est un peu comme expliquer vos blagues après les avoir racontées, que quelqu'un rit ou non. Soit dit en passant, les autres cultures scandinaves (finnoise, norvégienne et suédoise) sont les mêmes que le danois, à l'exception de la gestion très spéciale du "aa" - qui prouve bien sûr que le danois est la culture supérieure.
RenniePet
1
Pour ce que ça vaut, le danois trie ä et aa différemment en raison de l'emplacement des lettres spéciales æ (ae), ø (oe, ö) et å (aa, ä) à la fin de l'alphabet dans l'ordre écrit.
Alrekr
5

Voici un exemple où la comparaison d'égalité de chaîne utilisant InvariantCultureIgnoreCase et OrdinalIgnoreCase ne donnera pas les mêmes résultats:

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

Si vous exécutez ceci, equals1 sera faux et equals2 sera vrai.

Dwedit
la source
Juste pour ajouter un autre exemple similaire, mais avec des littéraux de chaîne, si a="\x00e9"(e aigu) et b="\x0065\x0301"(e combiné avec un accent aigu), StringComparer.Ordinal.Equals(a, b)renverra faux tandis que StringComparer.InvariantCulture.Equals(a, b)reviendra vrai.
George Helyar
2

Pas besoin d'utiliser des exemples de caractères unicode fantaisie pour montrer la différence. Voici un exemple simple que j'ai découvert aujourd'hui qui est surprenant, composé uniquement de caractères ASCII.

Selon le tableau ASCII, 0(0x48) est plus petit que _(0x95) en comparaison ordinaire. InvariantCulture dirait le contraire (code PowerShell ci-dessous):

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

Essayez toujours d'utiliser InvariantCulture dans les méthodes de chaîne qui l'acceptent comme surcharge. En utilisant InvariantCulture, vous êtes du bon côté. De nombreux programmeurs .NET peuvent ne pas utiliser cette fonctionnalité, mais si votre logiciel sera utilisé par différentes cultures, InvariantCulture est une fonctionnalité extrêmement pratique.

George
la source
3
Si votre logiciel ne sera pas utilisé par différentes cultures, il est cependant beaucoup plus lent qu'Ordinal.
Kyle
4
J'ai envisagé de voter en aval parce que vous n'avez certainement pas réfléchi à votre réponse au hasard. Bien qu'à l'intérieur, il y ait un grain de vérité. SI votre application est diffusée en masse dans plusieurs cultures ... Cela ne justifie certainement pas vos premiers mots "Essayez toujours d'utiliser InvariantCulture", n'est-ce pas? Je suis surpris que vous ne soyez pas revenu au fil des ans pour modifier cette folie après avoir reçu un downvote, et peut-être plus d'expérience.
Suamere