104 votes

Comment comparer des caractères Unicode qui se "ressemblent" ?

Je tombe dans un problème surprenant.

J'ai chargé un fichier texte dans mon application et j'ai une logique qui compare la valeur ayant µ.

Et j'ai réalisé que même si les textes sont identiques, la valeur de comparaison est fausse.

 Console.WriteLine("".Equals("µ")); // returns false
 Console.WriteLine("µ".Equals("µ")); // return true

Dans la ligne suivante, le caractère µ est copié-collé.

Cependant, ces personnages ne sont peut-être pas les seuls à être comme ça.

Existe-t-il un moyen en C# de comparer des caractères qui semblent identiques mais qui sont en fait différents ?

165 votes

On dirait que tu as trouvé le mu de Schrödinger.

19 votes

Il s'agit de caractères différents - même s'ils se ressemblent, ils ont des codes de caractères différents.

0 votes

Avez-vous essayé d'utiliser String.Compare("", "", StringComparison.Ordinal) (ou OrdinalIgnoreCase) ? Je pose la question parce que si vous faites une comparaison directe (non ordinale), alors les caractères seront toujours développés, puisque la façon dont le caractère est développé peut varier, vous pouvez voir des résultats différents.

151voto

Tony Points 4360

Parce qu'il s'agit de symboles vraiment différents, même s'ils se ressemblent, le premier est la lettre réelle et a des caractères. code = 956 (0x3BC) et le second est le micro signe et a 181 (0xB5) .

Références :

Ainsi, si vous souhaitez les comparer et qu'ils doivent être égaux, vous devez le faire manuellement, ou remplacer un caractère par un autre avant la comparaison. Ou bien utiliser le code suivant :

public void Main()
{
    var s1 = "";
    var s2 = "µ";

    Console.WriteLine(s1.Equals(s2));  // false
    Console.WriteLine(RemoveDiacritics(s1).Equals(RemoveDiacritics(s2))); // true 
}

static string RemoveDiacritics(string text) 
{
    var normalizedString = text.Normalize(NormalizationForm.FormKC);
    var stringBuilder = new StringBuilder();

    foreach (var c in normalizedString)
    {
        var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
        if (unicodeCategory != UnicodeCategory.NonSpacingMark)
        {
            stringBuilder.Append(c);
        }
    }

    return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}

Et le Démo

11 votes

Par curiosité, quelle est la raison d'être des deux symboles µ ? On ne voit pas de K dédié au nom de "signe kilo" (ou si ?).

12 votes

@MartinHaTh : Selon Wikipedia, c'est "pour des raisons historiques" .

12 votes

L'Unicode comporte de nombreux caractères de compatibilité issus d'anciens jeux de caractères (tels que ISO 8859-1 ), pour faciliter la conversion à partir de ces jeux de caractères. À l'époque où les jeux de caractères étaient limités à 8 bits, ils comprenaient quelques glyphes (comme certaines lettres grecques) pour les utilisations mathématiques et scientifiques les plus courantes. La réutilisation des glyphes en fonction de leur apparence était courante, de sorte qu'aucun "K" spécialisé n'a été ajouté. Mais il y avait toujours une solution de rechange ; le symbole correct pour "micro" est la minuscule grecque mu, le symbole correct pour Ohm est la majuscule omega, et ainsi de suite.

127voto

BoltClock Points 249668

Dans de nombreux cas, vous pouvez normaliser les deux caractères Unicode à une certaine forme de normalisation avant de les comparer, et ils devraient pouvoir correspondre. Bien entendu, la forme de normalisation que vous devez utiliser dépend des caractères eux-mêmes ; ce n'est pas parce qu'ils regardez de la même façon ne signifie pas nécessairement qu'ils représentent le même personnage. Vous devez également vous demander si cela est approprié pour votre cas d'utilisation - voir le commentaire de Jukka K. Korpela.

Pour cette situation particulière, si vous vous référez aux liens dans La réponse de Tony vous verrez que le tableau pour U+00B5 dit :

Décomposition <compat> PETIT LETTRE GREC MU (U+03BC)

Cela signifie que U+00B5, le deuxième caractère de votre comparaison originale, peut être décomposé en U+03BC, le premier caractère.

Vous normaliserez donc les caractères en utilisant la décomposition de la compatibilité totale, avec les formes de normalisation KC ou KD. Voici un exemple rapide que j'ai écrit pour démontrer :

using System;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        char first = '';
        char second = 'µ';

        // Technically you only need to normalize U+00B5 to obtain U+03BC, but
        // if you're unsure which character is which, you can safely normalize both
        string firstNormalized = first.ToString().Normalize(NormalizationForm.FormKD);
        string secondNormalized = second.ToString().Normalize(NormalizationForm.FormKD);

        Console.WriteLine(first.Equals(second));                     // False
        Console.WriteLine(firstNormalized.Equals(secondNormalized)); // True
    }
}

Pour plus de détails sur la normalisation d'Unicode et les différentes formes de normalisation, voir System.Text.NormalizationForm y la spécification Unicode .

26 votes

Merci pour le lien vers la spécification Unicode. C'est la première fois que je me renseigne sur ce sujet. Petite note de celui-ci : "Les formes de normalisation KC et KD ne doivent pas être appliquées aveuglément à un texte arbitraire Il est préférable de considérer ces formes de normalisation comme des correspondances entre majuscules et minuscules : elles sont utiles dans certains contextes pour identifier les significations essentielles, mais elles apportent également des modifications au texte qui ne sont pas toujours appropriées."

86voto

Vishal Suthar Points 7442

Ils ont tous deux des codes de caractères différents : Consultez cette page pour plus de détails

Console.WriteLine((int)'');  //956
Console.WriteLine((int)'µ');  //181

Où, le premier est :

Display     Friendly Code   Decimal Code    Hex Code    Description
====================================================================
           &mu;            &#956;          &#x3BC;     Lowercase Mu
µ           &micro;         &#181;          &#xB5;      micro sign Mu

Image

41voto

dan04 Points 33306

Pour l'exemple spécifique de (mu) et µ (micro signe), ce dernier a un décomposition de la compatibilité au premier, afin que vous puissiez normaliser la chaîne de caractères à FormKC o FormKD pour convertir les signes micro en mus.

Cependant, il existe de nombreux ensembles de caractères qui se ressemblent mais qui ne sont équivalents sous aucune forme de normalisation Unicode. Par exemple, A (Latin), (grec), et (Cyrillique). Le site web d'Unicode propose une confusables.txt avec une liste de ces derniers, destinée à aider les développeurs à se prémunir contre les attaques par homographe . Si nécessaire, vous pourriez analyser ce fichier et construire un tableau pour la "normalisation visuelle" des chaînes de caractères.

0 votes

C'est vraiment bon à savoir quand on utilise Normalize. Il semble surprenant qu'ils restent distincts.

4 votes

@user2864740 : Si un tau grec en majuscule ne restait pas distinct de la lettre T romaine, il serait très difficile de faire en sorte que le texte grec et le texte romain soient classés de manière raisonnable dans l'ordre alphabétique. De plus, si une police de caractères devait utiliser un style visuel différent pour les lettres grecques et romaines, il serait très gênant que les lettres grecques dont les formes ressemblent aux lettres romaines soient rendues différemment de celles qui ne le sont pas.

8 votes

Plus important encore, l'unification des alphabets européens rendrait ToUpper / ToLower difficile à mettre en œuvre. Il faut avoir "B".ToLower() sea b en anglais mais en grec et en russe. En l'état actuel des choses, seul le turc (sans point i ) et quelques autres langues ont besoin de règles de casse différentes de celles par défaut.

36voto

Subin Jacob Points 2197

Recherche les deux personnages dans un Base de données Unicode et voir le différence .

L'un est le Petite lettre grecque µ et l'autre est le Micro signe µ .

Name            : MICRO SIGN
Block           : Latin-1 Supplement
Category        : Letter, Lowercase [Ll]
Combine         : 0
BIDI            : Left-to-Right [L]
Decomposition   : <compat> GREEK SMALL LETTER MU (U+03BC)
Mirror          : N
Index entries   : MICRO SIGN
Upper case      : U+039C
Title case      : U+039C
Version         : Unicode 1.1.0 (June, 1993)

Name            : GREEK SMALL LETTER MU
Block           : Greek and Coptic
Category        : Letter, Lowercase [Ll]
Combine         : 0
BIDI            : Left-to-Right [L]
Mirror          : N
Upper case      : U+039C
Title case      : U+039C
See Also        : micro sign U+00B5
Version         : Unicode 1.1.0 (June, 1993)

4 votes

Comment cela a-t-il obtenu 37 votes positifs ? Il ne répond pas à la question ("Comment comparer des caractères unicodes"), il commente juste pourquoi cet exemple particulier n'est pas égal. Au mieux, cela devrait être un commentaire sur la question. Je comprends que les options de formatage des commentaires ne permettent pas de le publier aussi bien que les options de formatage des réponses, mais cela ne devrait pas être une raison valable pour le publier comme une réponse.

6 votes

En fait la question était différente, elle demandait pourquoi et µ le contrôle d'égalité retournait faux. Cette réponse y répond. Plus tard, OP a posé une autre question (cette question) : comment comparer deux caractères qui se ressemblent. Les deux questions avaient les meilleures réponses et plus tard, un des modérateurs a fusionné les deux questions en choisissant la meilleure réponse de la seconde comme la meilleure. Quelqu'un a édité cette question, de sorte qu'elle résumera

0 votes

En fait, je n'ai pas ajouté de contenu après la fusion.

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