web-dev-qa-db-fra.com

Comment comparer les 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 les mêmes, 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é.

Cependant, ce ne sont peut-être pas les seuls personnages qui ressemblent à ceci.

Existe-t-il un moyen en C # de comparer les personnages qui se ressemblent mais sont en réalité différents?

94
D J

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 sûr, la forme de normalisation que vous devez utiliser dépend des personnages eux-mêmes; ce n'est pas parce qu'ils se ressemblent qu'ils représentent nécessairement le même personnage. Vous devez également déterminer si cela convient à 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 réponse de Tony , vous verrez que le tableau pour + 00B5 dit:

Décomposition <compat> LETTRE MINUSCULE GRECQUE MU (U + 03BC)

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

Vous normaliserez donc les caractères en utilisant une décomposition de compatibilité complète, avec les formes de normalisation KC ou KD. Voici un exemple rapide que j'ai rédigé 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 Unicode et les différentes formes de normalisation, reportez-vous à System.Text.NormalizationForm et la spécification Unicode .

124
BoltClock

Parce que ce sont vraiment des symboles différents, même s'ils se ressemblent, le premier est la lettre réelle et a char code = 956 (0x3BC) et le second est le micro signe et a 181 (0xB5).

Les références:

Donc, si vous voulez les comparer et que vous avez besoin qu'ils soient égaux, vous devez le gérer manuellement ou remplacer un caractère par un autre avant la comparaison. Ou utilisez 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

149
Tony

Ils ont tous deux des codes de caractères différents: Reportez-vous à cela 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

86
Vishal Suthar

Pour l'exemple spécifique de μ (mu) et µ (signe micro), ce dernier a un décomposition de compatibilité au premier, donc vous pouvez normaliser la chaîne à FormKC ou FormKD pour convertir les micro-signes en mus.

Cependant, il existe de nombreux ensembles de caractères qui se ressemblent mais ne sont pas équivalents sous aucune forme de normalisation Unicode. Par exemple, A (latin), Α (Grec) et А (Cyrillique). Le site Unicode a un fichier confusables.txt avec une liste de ceux-ci, destiné à aider les développeurs à se prémunir attaques homographes . Si nécessaire, vous pouvez analyser ce fichier et créer un tableau de "normalisation visuelle" des chaînes.

38
dan04

Recherche les deux caractères dans une base de données Unicode et voyez la différence .

L'un est le lettre minuscule grecqueµ et l'autre est le Micro Signµ.

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)
34
Subin Jacob

Très probablement, il existe deux codes de caractères différents qui font (visiblement) le même caractère. Bien qu'ils ne soient pas techniquement égaux, ils semblent égaux. Jetez un œil à la table des caractères et voyez s'il existe plusieurs instances de ce caractère. Ou imprimez le code de caractère des deux caractères dans votre code.

9
PMF

Vous demandez "comment les comparer" mais vous ne nous dites pas ce que vous voulez faire.

Il existe au moins deux façons principales de les comparer:

Soit vous les comparez directement tels que vous êtes et ils sont différents

Ou vous utilisez la normalisation de compatibilité Unicode si vous avez besoin d'une comparaison qui les trouve correspondre.

Il pourrait y avoir un problème car la normalisation de la compatibilité Unicode rendra de nombreux autres caractères comparables. Si vous souhaitez que seuls ces deux personnages soient traités de la même manière, vous devez lancer vos propres fonctions de normalisation ou de comparaison.

Pour une solution plus spécifique, nous devons connaître votre problème spécifique. Dans quel contexte avez-vous rencontré ce problème?

6
hippietrail

Si je veux être pédant, je dirais que votre question n'a pas de sens, mais comme nous approchons de Noël et que les oiseaux chantent, je vais continuer.

Tout d'abord, les 2 entités que vous essayez de comparer sont glyphs, un glyphe fait partie d'un ensemble de glyphes fournis par ce qui est généralement connu comme une "police", la chose qui vient généralement dans un ttf, otf ou le format de fichier que vous utilisez.

Les glyphes sont une représentation d'un symbole donné, et comme ils sont une représentation qui dépend d'un ensemble spécifique, vous ne pouvez pas vous attendre à avoir 2 symboles identiques similaires ou même "meilleurs", c'est une phrase qui n'a pas de sens si vous tenez compte du contexte, vous devez au moins spécifier la police ou l'ensemble de glyphes que vous envisagez lorsque vous formulez une question comme celle-ci.

Ce qui est généralement utilisé pour résoudre un problème similaire à celui que vous rencontrez, c'est un OCR, essentiellement un logiciel qui reconnaît et compare les glyphes, si C # fournit un OCR par défaut, je ne le sais pas, mais c'est généralement une très mauvaise idée si vous n'avez pas vraiment besoin d'un OCR et que vous savez quoi en faire.

Vous pouvez éventuellement finir par interpréter un livre de physique comme un livre grec ancien sans mentionner le fait que l'OCR est généralement coûteux en termes de ressources.

Il y a une raison pour laquelle ces personnages sont localisés de la façon dont ils sont localisés, ne le faites tout simplement pas.

5
user2485710

Il est possible de dessiner les deux caractères avec le même style de police et la même taille avec la méthode DrawString. Après la génération de deux bitmaps avec symboles, il est possible de les comparer pixel par pixel.

L'avantage de cette méthode est que vous pouvez comparer non seulement des caractères égaux absolus, mais aussi similaires (avec une tolérance définie).

1
Ivan Kochurkin