web-dev-qa-db-fra.com

Différence entre la comparaison de chaînes InvariantCulture et Ordinal

Quand on compare deux chaînes de c # pour l'égalité, quelle est la différence entre la comparaison InvariantCulture et Ordinal?

504
Kapil

Culture invariante

Utilise un ensemble "standard" d'ordonnancement des 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', en fonction de la localisation, etc.).

Ordinal

D'autre part, regarde uniquement les valeurs du ou des octets bruts représentant le caractère.


Il existe un excellent échantillon à l'adresse http://msdn.Microsoft.com/en-us/library/e6883c06.aspx , qui affiche les résultats des différentes valeurs de StringComparison. Tout au long à 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 les rendements d'InvariantCulture (U + 0069, U + 0049, U + 00131), les rendements ordinaux (U + 0049, U + 0069, U + 00131).

280
JaredReisinger

C'est important, par exemple - il y a une chose appelée expansion du personnage

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

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

Avec InvariantCulture, le caractère ß est étendu à ss.

218
Ventsyslav Raikov

Pointant vers Meilleures pratiques pour l’utilisation de chaînes dans le .NET Framework :

  • Utilisez StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase pour les comparaisons en tant que valeur par défaut sûre pour la correspondance de chaînes dépendant de la culture.
  • Utilisez des comparaisons avec StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase pour de meilleures performances.
  • Utilisez les valeurs non linguistiques StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase au lieu d'opérations de chaîne basées sur CultureInfo.InvariantCulture lorsque la comparaison est linguistiquement non pertinente (symbolique, par exemple).

Et enfin:

  • N'utilisez pas d'opérations de chaîne basées sur StringComparison.InvariantCulture dans la plupart des cas . Une des rares exceptions concerne la persistance de données linguistiquement significatives, mais culturellement agnostiques.
88
Dariusz

Une autre différence pratique (en anglais, où les accents sont peu communs) est qu'une comparaison InvariantCulture compare les chaînes entières en respectant les majuscules, et ensuite, si nécessaire (et demandée), distingue par cas après avoir d'abord comparé uniquement les lettres distinctes. (Vous pouvez également faire une comparaison insensible à la casse, bien sûr, sans distinction de casse.) Corrigé: Les lettres accentuées sont considérées comme une autre version des mêmes lettres et la chaîne est comparée en premier. ignorer les accents et les comptabiliser ensuite si les lettres générales correspondent toutes (de la même manière que pour des cas différents, sauf que finalement, elles ne sont pas ignorées dans une comparaison insensible à la casse). Cela regroupe les versions accentuées du même mot proches les unes des autres au lieu d'être complètement séparées à la première différence d'accent. C'est l'ordre de tri que l'on trouve généralement dans un dictionnaire, les mots en majuscules apparaissant juste à côté de leurs équivalents minuscules et les lettres accentuées se trouvant à proximité de la lettre non accentuée correspondante.

Une comparaison ordinale compare strictement les valeurs des caractères numériques, en s’arrêtant à la première différence. Ceci trie les lettres majuscules complètement séparées des minuscules (et les lettres accentuées, probablement séparées de celles-ci), de sorte que les mots en majuscule ne feraient aucun tri près de leurs équivalents minuscules.

InvariantCulture considère également que les majuscules sont plus grandes que les minuscules, alors qu'Ordinal considère que les majuscules sont moins que les minuscules (une réserve de ASCII de l'époque ancienne avant que les ordinateurs ne soient en minuscule, les majuscules ont été attribuées en premier et ont donc valeurs inférieures aux lettres minuscules ajoutées ultérieurement).

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"

56
Rob Parker

Bien que la question porte sur égalité, pour une référence visuelle rapide, voici l'ordre de certaines chaînes trié en utilisant quelques cultures illustrant certaines des idiosyncrasies existantes.

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

Observations:

  • de-DE, ja-JP et en-US trient de la même manière
  • Invariant ne trie que ss et ß différemment des trois cultures précédentes
  • da-DK trie assez différemment
  • le drapeau IgnoreCase est important pour toutes les cultures de l'échantillon

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));
}
29
Eugene Beresovsky

L'invariant est un type de comparaison approprié sur le plan linguistique.
Ordinal est un type de comparaison binaire. (plus rapide)
Voir http://www.siao2.com/2004/12/29/344136.aspx

26
DanH

Voici un exemple où la comparaison d'égalité de chaînes à l'aide d'InvariantCultureIgnoreCase et d'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.

5
Dwedit

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 et qui est surprenant, composé uniquement de ASCII caractères.

Selon la table ASCII, 0 (0x48) est inférieur à _ (0x95) par rapport à la moyenne. InvariantCulture dirait le contraire (code PowerShell ci-dessous):

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