2010-11-10 24 views
3

J'ai deux chaînes, l'une avec une valeur à deux octets et l'autre, une seule octet. Le résultat de la comparaison de chaînes renvoie false, comment puis-je les comparer correctement après avoir ignoré la différence d'un octet/double octet?Comparaison de chaînes à deux octets en C#

string s1 = "smatsumoto11" 
string s2 = "smatsumoto11" 

Dans le même scénario, si vous avez une colonne nvarchar dans le serveur SQL qui contient la valeur smatsumoto11, une requête pour récupérer les données avec l'état où la chaîne ayant smatsumoto11 retourne la même ligne. J'ai besoin d'une sémantique similaire avec la comparaison de chaînes C#.

J'ai essayé quelques options mentionnées sur MSDN mais elles ne semblent pas fonctionner.

Des idées?

+3

"J'ai essayé quelques options mentionnées sur MSDN mais elles ne semblent pas fonctionner." Vous devez nous dire ce que vous avez essayé. –

+3

En outre, d'où obtenez-vous la chaîne à un octet? –

+0

En outre, si la chaîne à un octet est stockée dans un objet C# 'string', elle est déjà sous forme 2 octets. Peut-être que les chaînes proviennent de pages de code différentes, ou de codages différents, et ne se comparent donc pas comme prévu. –

Répondre

5

Votre s1 contient soi-disant caractères « » pleine largeur, de sorte que vous pouvez utiliser string.Compare et lui dire d'ignorer largeur de caractère:

string.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreWidth); 

(Bien sûr, spécifiez si un autre CultureInfo nécessaire.)

+0

Fonctionne comme je le voulais .... Merci beaucoup – Metallikanz

+0

Encore une chose .... est-il possible de convertir les caractères "pleine largeur" ​​en un normal? – Metallikanz

+0

S'il vous plaît ignorer la question .... Utilisé String.Normalize() tel que visé par GvS ci-dessous. – Metallikanz

1

Ma machine dit que s1 est dans MS Mincho.

MS Mincho (MS 明朝) - distribué avec la version japonaise de Windows 3.1 ou version ultérieure, certaines versions d'Internet Explorer 3 Japanese Font Pack, toutes les régions dans Windows XP, Microsoft Office vX à 2004.

Ce qui suit est totalement obsolète par la réponse de Arnout.

Je connais un truc qui fonctionne comme //TRANSLIT dans iconv et qui semble fonctionner ici.

 string s1 = "smatsumoto11"; 
     string s2 = "smatsumoto11"; 

     string conv = Encoding.ASCII.GetString(Encoding.GetEncoding("Cyrillic").GetBytes(s1)); 

     if (conv == s2) Console.WriteLine("They are the same!"); 

Un jour, je dois vraiment essayer de savoir comment cela fonctionne ...

+0

La sélection de police correspond à la façon dont votre navigateur a choisi de traiter ces caractères. –

+0

Oui, je suppose que c'était plus comme un commentaire qu'une réponse. –

3

Avant de faire la comparaison, vous pouvez essayer de « Normalize » vos cordes:

Renvoie une nouvelle chaîne dont la valeur textuelle est la même que cette chaîne, mais dont la représentation binaire est sous la forme de normalisation Unicode spécifié.

Certains caractères Unicode ont plusieurs représentations binaires équivalentes constituées d'ensembles de caractères Unicode composites et/ou composites. L'existence de plusieurs représentations pour un seul caractère complique la recherche, le tri, l'appariement et d'autres opérations.

+0

Merci pour l'indice .... – Metallikanz

0

Alors que les accepted answer œuvres, et est correcte en ce qui concerne la question principale étant des caractères « larges », il y a quelques idées fausses et les aspects techniques de la question qui devraient être abordés afin d'avoir une meilleure compréhension de ce qui est vraiment se passe ici, à la fois dans .NET et dans SQL Server.

Première:

J'ai deux chaînes avec une valeur double octet et l'autre est un seul octet un.

Non, vous ne le faites pas. Vous avez deux chaînes Unicode, codées comme UTF-16 Little Endian (qui fonctionne comme Windows et .NET). Et en termes pratiques, la plupart du temps, les caractères sont codés sur deux octets, ce qui ne couvre que 62 000 à 63 000 caractères (soit les points de code entre U + 0000 et U + FFFF - ou 0 - 65 535 - sont des caractères "valides"). Mais Unicode permet de mapper un peu plus de 1,1 million de points de code, et actuellement il en compte un peu plus de 260 000 already mapped. Les points de code au-dessus de U + FFFF/65,535, connus sous le nom de caractères supplémentaires, sont mappés à des ensembles de deux valeurs à deux octets connus sous le nom de paires de substitution. Ainsi, alors qu'ils sont moins fréquemment utilisés, la majorité des points de code Unicode sont en réalité de 4 octets.

Deuxième:

Le résultat de la comparaison de chaînes de caractères faux, comment puis-je les amener à comparer correctement

Les lettres s1 = "smatsumoto11" sont appelés caractères « » pleine chasse. Vous pouvez voir la liste complète d'entre eux ici:

http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:East_Asian_Width=Fullwidth:]

Quelques explications pour lesquelles il existe différentes largeurs, en premier lieu se trouvent ici:

http://unicode-table.com/en/blocks/halfwidth-and-fullwidth-forms/

Si vous voulez comparez les deux chaînes de la Question pour qu'elles soient égales, vous pouvez soit utiliser la méthode String.Compare(String, String, CultureInfo, CompareOptions) comme mentionné dans la réponse @ Arnout, soit utiliser CompareInfo.Compare(String, String, CompareOptions) comme suit:

CompareInfo.Compare(s1, s2, CompareOptions.IgnoreWidth) 

Troisième:

Dans le même scénario, si vous avez une colonne nvarchar dans le serveur SQL qui contient la valeur smatsumoto11, une requête pour récupérer les données avec la condition where ayant la chaîne smatsumoto11 retournera la même rangée.

Ceci est une manière potentiellement dangereuse de penser à des comparaisons de chaînes. Il n'y a pas de façon particulière que les chaînes se comparent dans pratiquement n'importe quelle base de données, sauf si les chaînes sont en ASCII 7 bits (valeurs 0 - 127) qui n'incluent même pas les Pages de Code, et je ne sais pas . Les comparaisons sont basées sur le LCID/Locale/Culture/Collation particulier. Le classement par défaut dans SQL Server (au moins aux États-Unis) est SQL_Latin1_General_CP1_CI_AS, qui est sensible à la casse et sensible aux accents. Il utilise également la page de code 1252 (qui affecte les données CHAR/VARCHAR, pas NCHAR/NVARCHAR) et la culture «en-US». Les classements pour d'autres cultures/LCID peuvent ne pas égaler Fullwidth et "half-width". Et, les classements qui ont _WS dans leur nom ne correspondraient certainement pas à ces deux chaînes car _WS signifie «Largeur sensible», qui est la valeur par défaut pour les comparaisons .NET si vous ne spécifiez pas l'option CompareOptions.IgnoreWidth.

Si vous exécutez la requête suivante pour trouver les classements qui ont _WS en leur nom, vous constaterez qu'il ya 1776 sur 3885 Les classements totaux qui correspondent qui sont Width sensibles et serait pas match de ces deux chaînes (à moins dans SQL Server 2012). Bien sûr, il y a également 262 classements binaires (c'est-à-dire les noms se terminant soit par le _BIN obsolète ou le _BIN2 préféré) qui n'égaleraient pas non plus ces chaînes, mais ce n'est pas un problème de sensibilité de largeur.

SELECT * 
FROM sys.fn_helpcollations() 
WHERE [name] LIKE N'%[_]WS%' 
ORDER BY [name]; 
-- 1776 out of 3885 on SQL Server 2012 

En outre, comme je viens de mentionner, le malheureux (et dépréciée) par défaut Collation de SQL_Latin1_General_CP1_CI_AS, ou même la meilleure version de Latin1_General_100_CI_AS, est insensible à la casse. Donc les chaînes que vous comparez sont toutes minuscules donc elles sont égales quand vous utilisez juste CompareOptions.IgnoreWidth, mais si vous voulez émuler ces classements particuliers dans SQL Server, alors le comportement par défaut de .NET pour être sensible à la casse ne correspondrait pas au SQL Comportement du serveur Afin de mieux correspondre au comportement SQL Server (au moins pour les classements, ou tout marqué comme ayant _CI et pas ayant _WS, vous devez inclure également l'option CompareOptions.IgnoreCase comme suit:

CompareInfo.Compare(s1, s2, CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) 

// or 

String.Compare(s1, s2, CultureInfo.CurrentCulture,  
       CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) 

Ressources supplémentaires:

Comparing Strings in the .NET Framework

Best Practices for Using Strings in the .NET Framework