web-dev-qa-db-fra.com

Les comparaisons de chaînes peuvent-elles vraiment différer en fonction de la culture lorsque la chaîne est garantie de ne pas changer?

Je lis les informations d'identification/chaînes de connexion chiffrées à partir d'un fichier de configuration. Resharper me dit, "String.IndexOf (string) est spécifique à la culture ici" sur cette ligne:

if (line.Contains("Host=")) {
    _Host = line.Substring(line.IndexOf(
        "Host=") + "Host=".Length, line.Length - "Host=".Length);

... et souhaite donc le changer en:

if (line.Contains("Host=")) {
    _Host = line.Substring(line.IndexOf("Host=", System.StringComparison.Ordinal) + "Host=".Length, line.Length -   "Host=".Length);

La valeur que je lis sera toujours "Host =" quel que soit l'endroit où l'application peut être déployée. Est-il vraiment judicieux d'ajouter ce bit "System.StringComparison.Ordinal"?

Plus important encore, cela pourrait-il blesser quelque chose (pour l'utiliser)?

53
B. Clay Shannon

Absolument. Par MSDN ( http://msdn.Microsoft.com/en-us/library/d93tkzah.aspx ),

Cette méthode effectue une recherche Word (sensible à la casse et sensible à la culture ) en utilisant la culture actuelle.

Ainsi, vous pouvez obtenir des résultats différents si vous l'exécutez sous une culture différente (via les paramètres régionaux et linguistiques dans le Panneau de configuration).

Dans ce cas particulier, vous n'aurez probablement pas de problème, mais jetez un i dans la chaîne de recherche et exécutez-le en Turquie et cela gâchera probablement votre journée.

Voir MSDN: http://msdn.Microsoft.com/en-us/library/ms973919.aspx

Ces nouvelles recommandations et API existent pour alléger les hypothèses erronées sur le comportement des API de chaîne par défaut. L'exemple canonique de bogues émergeant où les données de chaînes non linguistiques sont interprétées linguistiquement est le problème "Turkish-I".

Pour presque tous les alphabets latins, y compris l'anglais américain, le caractère i (\ u0069) est la version minuscule du caractère I (\ u0049). Cette règle de casse devient rapidement la valeur par défaut pour quelqu'un qui programme dans une telle culture. Cependant, en turc ("tr-TR"), il existe un caractère "i avec un point" majuscule (\ u0130), qui est la version majuscule de i. De même, en turc, il y a un "i sans point" en minuscule, ou (\ u0131), qui met en majuscule à I. Ce comportement se produit également dans la culture azérie ("az").

Par conséquent, les hypothèses normalement faites sur la mise en majuscule de i ou la minuscule de I ne sont pas valables dans toutes les cultures. Si les surcharges par défaut pour les routines de comparaison de chaînes sont utilisées, elles seront sujettes à des variations entre les cultures. Pour les données non linguistiques, comme dans l'exemple suivant, cela peut produire des résultats indésirables:

    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US")
Console.WriteLine("Culture = {0}",
   Thread.CurrentThread.CurrentCulture.DisplayName);
Console.WriteLine("(file == FILE) = {0}", 
   (String.Compare("file", "FILE", true) == 0));

Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
Console.WriteLine("Culture = {0}",
   Thread.CurrentThread.CurrentCulture.DisplayName);
Console.WriteLine("(file == FILE) = {0}", 
   (String.Compare("file", "FILE", true) == 0));

En raison de la différence de la comparaison de I, les résultats des comparaisons changent lorsque la culture de thread est modifiée. Voici la sortie:

Culture = English (United States)
(file == FILE) = True
Culture = Turkish (Turkey)
(file == FILE) = False

Voici un exemple sans boîtier:

var s1 = "é"; //é as one character (ALT+0233)
var s2 = "é"; //'e', plus combining acute accent U+301 (two characters)

Console.WriteLine(s1.IndexOf(s2, StringComparison.Ordinal)); //-1
Console.WriteLine(s1.IndexOf(s2, StringComparison.InvariantCulture)); //0
Console.WriteLine(s1.IndexOf(s2, StringComparison.CurrentCulture)); //0
64
Mark Sowul

CA1309: UseOrdinalStringComparison

Ce n'est pas blesser de ne pas l'utiliser, mais "en définissant explicitement le paramètre sur StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase, votre code gagne souvent en vitesse, augmente la justesse et devient plus fiable.".


Qu'est-ce que l'Ordinal exactement et pourquoi est-ce important pour votre cas?

Une opération qui utilise des règles de tri ordinales effectue une comparaison basée sur la valeur numérique (point de code Unicode) de chaque caractère de la chaîne. Une comparaison ordinale est rapide mais insensible à la culture. Lorsque vous utilisez des règles de tri ordinales pour trier les chaînes commençant par des caractères Unicode (U +), la chaîne U + xxxx précède la chaîne U + yyyy si la valeur de xxxx est numériquement inférieure à yyyy.

Et, comme vous l'avez dit ... la valeur de chaîne dans laquelle vous lisez n'est pas sensible à la culture, il est donc logique d'utiliser une comparaison ordinale par opposition à une comparaison Word. N'oubliez pas, Ordinal signifie "ce n'est pas sensible à la culture".

27
m-y

Pour répondre à votre question spécifique: Non, mais un outil d'analyse statique ne pourra pas se rendre compte que votre valeur d'entrée ne contiendra jamais d'informations spécifiques aux paramètres régionaux.