42 votes

Bug dans la comparaison de chaînes du .NET Framework

C'est une exigence pour toute comparaison sorte de travail que l'ordre sous-jacent de l'opérateur est transitive et antisymétrique.

Dans .NET, ce n'est pas vrai pour certaines chaînes:

static void CompareBug()
{
  string x = "\u002D\u30A2";  // or just "-ア" if charset allows
  string y = "\u3042";        // or just "あ" if charset allows

  Console.WriteLine(x.CompareTo(y));  // positive one
  Console.WriteLine(y.CompareTo(x));  // positive one
  Console.WriteLine(StringComparer.InvariantCulture.Compare(x, y));  // positive one
  Console.WriteLine(StringComparer.InvariantCulture.Compare(y, x));  // positive one

  var ja = StringComparer.Create(new CultureInfo("ja-JP", false), false);
  Console.WriteLine(ja.Compare(x, y));  // positive one
  Console.WriteLine(ja.Compare(y, x));  // positive one
}

Vous voyez qu' x est strictement plus grand que y, et y est strictement plus grand que x.

Parce qu' x.CompareTo(x) et ainsi de suite tous les donner à zéro (0), il est clair que ce n'est pas un ordre. Il n'est pas surprenant, j'obtiens des résultats imprévisibles quand j' Sort des tableaux ou des listes contenant des chaînes de caractères comme x et y. Si je n'ai pas testé, j'en suis sûr, SortedDictionary<string, WhatEver> ont des problèmes de maintien de soi dans l'ordre de tri et/ou de la localisation des éléments si des chaînes de caractères comme x et y sont utilisés pour les clés.

Est-ce un bug bien connu? Quelles sont les versions du framework sont touchés (j'essaye de faire avec .NET 4.0)?

EDIT:

Voici un exemple où le signe est négatif de la manière suivante:

x = "\u4E00\u30A0";         // equiv: "一゠"
y = "\u4E00\u002D\u0041";   // equiv: "一-A"

17voto

shuribot Points 250

Si un tri correct est si importante dans votre problème, il suffit d'utiliser l'ordinal de comparaison de chaînes de caractères à la place de la culture sensible. Seulement celui-ci garantit transitive et antisymétrique la comparaison que vous voulez.

Ce MSDN dit:

La spécification de la StringComparison.Ordinale ou StringComparison.OrdinalIgnoreCase valeur dans un appel de méthode correspond à un non-comparaison linguistique dans lequel les caractéristiques des langues naturelles sont ignorés. Les méthodes qui sont invoqués avec ces StringComparison les valeurs de chaîne de base de fonctionnement des décisions sur simple octet comparaisons au lieu d'emballer ou d'équivalence des tables qui sont paramétrées par la culture. Dans la plupart des cas, cette approche correspond le mieux au but l'interprétation des chaînes tout en rendant le code plus rapide et plus fiable.

Et il fonctionne comme prévu:

    Console.WriteLine(String.Compare(x, y, StringComparison.Ordinal));  // -12309
    Console.WriteLine(String.Compare(y, x, StringComparison.Ordinal));  // 12309

Oui, ça n'explique pas pourquoi sensible à la culture, la comparaison donne des résultats incohérents. Eh bien, étrange culture - résultat étrange.

1voto

Alex Points 1422

Je suis tombé sur ce post DONC, pendant que j'étais à essayer de comprendre pourquoi j'ai eu des problèmes pour récupérer (string) touches qui ont été insérés dans un SortedList, après que j'ai découvert la cause en était l'étrange comportement de l' .Net 40 et au-dessus de comparateurs (a1 < a2 et a2 < a3, mais a1 > a3).

Mon combat pour comprendre ce qui se passait peut être trouvé ici: c# SortedList<string, TValue>.ContainsKey de succès pour ajouter une touche renvoie la valeur false.

Vous voudrez peut-être avoir un coup d'oeil à la "UPDATE 3" section de mes question. Il semble que le problème a été signalé à Microsoft en Décembre 2012, et fermée avant la fin du mois de janvier 2013 "ne sera pas corrigé". En outre, il énumère une solution de contournement qui peuvent être utilisés.

J'ai créé une mise en œuvre de cette solution de contournement recommandée, et vérifié qu'il a résolu le problème que j'avais rencontré. J'ai également vérifié que cela résout le problème que vous avez signalé.

public static void SO_13254153_Question()
{
    string x = "\u002D\u30A2";  // or just "-ア" if charset allows
    string y = "\u3042";        // or just "あ" if charset allows        

    var invariantComparer = new WorkAroundStringComparer();
    var japaneseComparer = new WorkAroundStringComparer(new System.Globalization.CultureInfo("ja-JP", false));
    Console.WriteLine(x.CompareTo(y));  // positive one
    Console.WriteLine(y.CompareTo(x));  // positive one
    Console.WriteLine(invariantComparer.Compare(x, y));  // negative one
    Console.WriteLine(invariantComparer.Compare(y, x));  // positive one
    Console.WriteLine(japaneseComparer.Compare(x, y));  // negative one
    Console.WriteLine(japaneseComparer.Compare(y, x));  // positive one
}

Le problème reste que cette solution de contournement est tellement lent qu'il n'est guère pratique pour une utilisation avec de grands ensembles de chaînes de caractères. Donc j'espère que Microsoft va reconsidérer la fermeture de ce problème ou que quelqu'un sait d'une meilleure solution de contournement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X