webentwicklung-frage-antwort-db.com.de

Wie vergleiche ich Unicode-Zeichen, die sich ähneln?

Ich gerate in ein überraschendes Problem.

Ich habe eine Textdatei in meine Anwendung geladen und ich habe eine Logik, die den Wert mit µ vergleicht.

Und mir wurde klar, dass der Vergleichswert auch bei gleichen Texten falsch ist.

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

In der späteren Zeile wird das Zeichen µ kopiert und eingefügt.

Dies sind jedoch möglicherweise nicht die einzigen Zeichen, die so sind.

Gibt es in C # eine Möglichkeit, die Zeichen zu vergleichen, die gleich aussehen, sich aber tatsächlich unterscheiden?

94
D J

In vielen Fällen können Sie normalisieren beide Unicode-Zeichen zu einer bestimmten Normalisierungsform, bevor Sie sie vergleichen, und sie sollten übereinstimmen können. Welche Normalisierungsform Sie verwenden müssen, hängt natürlich von den Zeichen selbst ab. Nur weil sie gleich aussehen , bedeutet dies nicht unbedingt, dass sie den gleichen Charakter repräsentieren. Sie müssen auch überlegen, ob es für Ihren Anwendungsfall geeignet ist - siehe den Kommentar von Jukka K. Korpela.

In dieser speziellen Situation sehen Sie, wenn Sie auf die Links in Tonys Antwort verweisen, dass in der Tabelle für + 00B5 steht:

Zerlegung <compat> GREEK SMALL LETTER MU (U + 03BC)

Dies bedeutet, dass U + 00B5, das zweite Zeichen in Ihrem ursprünglichen Vergleich, in U + 03BC, das erste Zeichen, zerlegt werden kann.

Sie normalisieren die Zeichen also mithilfe der vollständigen Kompatibilitätszerlegung mit den Normalisierungsformen KC oder KD. Hier ist ein kurzes Beispiel, das ich geschrieben habe, um zu demonstrieren:

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
    }
}

Ausführliche Informationen zur Unicode-Normalisierung und zu den verschiedenen Normalisierungsformen finden Sie unter System.Text.NormalizationForm und die Unicode-Spezifikation .

124
BoltClock

Da es sich wirklich um verschiedene Symbole handelt, obwohl sie gleich aussehen, ist das erste der tatsächliche Buchstabe und hat das Zeichen code = 956 (0x3BC) und das zweite das Mikrozeichen und hat das Zeichen 181 (0xB5).

Verweise:

Wenn Sie sie vergleichen möchten und sie gleich sein müssen, müssen Sie sie manuell verarbeiten oder ein Zeichen vor dem Vergleich durch ein anderes ersetzen. Oder verwenden Sie den folgenden Code:

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);
}

Und die Demo

149
Tony

Beide haben unterschiedliche Zeichencodes: Weitere Informationen finden Sie hier

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

Wo ist die erste:

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

Image

86
Vishal Suthar

Für das spezifische Beispiel von μ (mu) und µ (Mikrozeichen), letzteres hat eine Kompatibilitätszerlegung zum ersteren, also können Sie normalisieren den String zu FormKC oder FormKD, um die Mikrozeichen in mus umzuwandeln.

Es gibt jedoch viele Zeichensätze, die sich ähneln, jedoch in keiner Unicode-Normalisierungsform gleichwertig sind. Zum Beispiel A (lateinisch), Α (Griechisch) und А (Kyrillisch). Die Unicode-Website enthält eine confusables.txt -Datei mit einer Liste dieser Dateien, die Entwicklern helfen soll, sich vor Homograph-Angriffen zu schützen. Bei Bedarf können Sie diese Datei analysieren und eine Tabelle für die „visuelle Normalisierung“ von Zeichenfolgen erstellen.

38
dan04

Suche beide Zeichen in einer nicode-Datenbank und sehen Sie den Unterschied .

Einer ist der griechischer Kleinbuchstabeµ und das andere ist das Mikrozeichenµ.

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

[~ # ~] edit [~ # ~] Nach dem Zusammenführen dieser Frage mit Vergleich von 'μ' und 'µ') in C #
Ursprüngliche Antwort gepostet:

_ "μ".ToUpper().Equals("µ".ToUpper()); //This always return true.
_

[~ # ~] edit [~ # ~] Nach dem Lesen der Kommentare ist es nicht gut, die obige Methode zu verwenden, da sie möglicherweise falsche Ergebnisse liefert für einen anderen Eingabetyp sollten wir normalize verwenden, wobei die vollständige Kompatibilitätszerlegung verwendet wird, wie in wiki erwähnt. (Dank der Antwort von BoltClock )

_    static string GREEK_SMALL_LETTER_MU = new String(new char[] { '\u03BC' });
    static string MICRO_SIGN = new String(new char[] { '\u00B5' });

    public static void Main()
    {
        string Mus = "µμ";
        string NormalizedString = null;
        int i = 0;
        do
        {
            string OriginalUnicodeString = Mus[i].ToString();
            if (OriginalUnicodeString.Equals(GREEK_SMALL_LETTER_MU))
                Console.WriteLine(" INFORMATIO ABOUT GREEK_SMALL_LETTER_MU");
            else if (OriginalUnicodeString.Equals(MICRO_SIGN))
                Console.WriteLine(" INFORMATIO ABOUT MICRO_SIGN");

            Console.WriteLine();
            ShowHexaDecimal(OriginalUnicodeString);                
            Console.WriteLine("Unicode character category " + CharUnicodeInfo.GetUnicodeCategory(Mus[i]));

            NormalizedString = OriginalUnicodeString.Normalize(NormalizationForm.FormC);
            Console.Write("Form C Normalized: ");
            ShowHexaDecimal(NormalizedString);               

            NormalizedString = OriginalUnicodeString.Normalize(NormalizationForm.FormD);
            Console.Write("Form D Normalized: ");
            ShowHexaDecimal(NormalizedString);               

            NormalizedString = OriginalUnicodeString.Normalize(NormalizationForm.FormKC);
            Console.Write("Form KC Normalized: ");
            ShowHexaDecimal(NormalizedString);                

            NormalizedString = OriginalUnicodeString.Normalize(NormalizationForm.FormKD);
            Console.Write("Form KD Normalized: ");
            ShowHexaDecimal(NormalizedString);                
            Console.WriteLine("_______________________________________________________________");
            i++;
        } while (i < 2);
        Console.ReadLine();
    }

    private static void ShowHexaDecimal(string UnicodeString)
    {
        Console.Write("Hexa-Decimal Characters of " + UnicodeString + "  are ");
        foreach (short x in UnicodeString.ToCharArray())
        {
            Console.Write("{0:X4} ", x);
        }
        Console.WriteLine();
    }
_

Ausgabe

_INFORMATIO ABOUT MICRO_SIGN    
Hexa-Decimal Characters of µ  are 00B5
Unicode character category LowercaseLetter
Form C Normalized: Hexa-Decimal Characters of µ  are 00B5
Form D Normalized: Hexa-Decimal Characters of µ  are 00B5
Form KC Normalized: Hexa-Decimal Characters of µ  are 03BC
Form KD Normalized: Hexa-Decimal Characters of µ  are 03BC
 ________________________________________________________________
 INFORMATIO ABOUT GREEK_SMALL_LETTER_MU    
Hexa-Decimal Characters of µ  are 03BC
Unicode character category LowercaseLetter
Form C Normalized: Hexa-Decimal Characters of µ  are 03BC
Form D Normalized: Hexa-Decimal Characters of µ  are 03BC
Form KC Normalized: Hexa-Decimal Characters of µ  are 03BC
Form KD Normalized: Hexa-Decimal Characters of µ  are 03BC
 ________________________________________________________________
_

Beim Lesen von Informationen in nicode_equivalence habe ich gefunden

Die Auswahl der Äquivalenzkriterien kann sich auf die Suchergebnisse auswirken. Zum Beispiel einige typografische Ligaturen wie U + FB03 (ffi), ..... also würde eine Suche nach U + 0066 (f) als Teilzeichenfolge erfolgreich in einer [~ # ~] nfkc [~ # ~] Normalisierung von U + FB03 aber nicht in [~ # ~] nfc [~ # ~] Normalisierung von U + FB03.

Um die Äquivalenz zu vergleichen, sollten wir normalerweise FormKC dh NFKC-Normalisierung oder FormKD dh NFKD-Normalisierung.
Ich war ein bisschen neugierig, mehr über alle Unicode-Zeichen zu erfahren, also habe ich ein Beispiel erstellt, das alle Unicode-Zeichen in _UTF-16_ durchläuft, und ich habe einige Ergebnisse erhalten, die ich diskutieren möchte

  • Informationen zu Zeichen, deren normalisierte Werte FormC und FormD nicht äquivalent waren
    _Total: 12,118_
    Character (int value): 192-197, 199-207, 209-214, 217-221, 224-253, ..... 44032-55203
  • Informationen zu Zeichen, deren normalisierte Werte FormKC und FormKD nicht äquivalent waren
    _Total: 12,245_
    Character (int value): 192-197, 199-207, 209-214, 217-221, 224-228, ..... 44032-55203, 64420-64421, 64432-64433, 64490-64507, 64512-64516, 64612-64617, 64663-64667, 64735-64736, 65153-65164, 65269-65274
  • Alle Zeichen, deren normalisierter Wert FormC und FormD nicht äquivalent waren, waren mit Ausnahme dieser Zeichen auch nicht äquivalent, da die normalisierten Werte FormKC und FormKD nicht äquivalent waren
    Charaktere: _901 '΅', 8129 '῁', 8141 '῍', 8142 '῎', 8143 '῏', 8157 '῝', 8158 '῞'_
    _, 8159 '῟', 8173 '῭', 8174 '΅'_
  • Zusätzliche Zeichen, deren normalisierter Wert FormKC und FormKD nicht äquivalent waren, aber dort waren die normalisierten Werte FormC und FormD äquivalent
    _Total: 119_
    Charaktere: _452 'DŽ' 453 'Dž' 454 'dž' 12814 '㈎' 12815 '㈏' 12816 '㈐' 12817 '㈑' 12818 '㈒' 12819 '㈓' 12820 '㈔' 12821 '㈕', 12822 '㈖' 12823 '㈗' 12824 '㈘' 12825 '㈙' 12826 '㈚' 12827 '㈛' 12828 '㈜' 12829 '㈝' 12830 '㈞' 12910 '㉮' 12911 '㉯' 12912 '㉰' 12913 '㉱' 12914 '㉲' 12915 '㉳' 12916 '㉴' 12917 '㉵' 12918 '㉶' 12919 '㉷' 12920 '㉸' 12921 '㉹' 12922 '㉺' 12923 '㉻' 12924 '㉼' 12925 '㉽' 12926 '㉾' 13056 '㌀' 13058 '㌂' 13060 '㌄' 13063 '㌇' 13070 '㌎' 13071 '㌏' 13072 '㌐' 13073 '㌑' 13075 '㌓' 13077 '㌕' 13080 '㌘' 13081 '㌙' 13082 '㌚' 13086 '㌞' 13089 '㌡' 13092 '㌤' 13093 '㌥' 13094 '㌦' 13099 '㌫' 13100 '㌬' 13101 '㌭' 13102 '㌮' 13103 '㌯' 13104 '㌰' 13105 '㌱' 13106 '㌲' 13108 '㌴' 13111 '㌷' 13112 '㌸' 13114 '㌺' 13115 '㌻' 13116 '㌼' 13117 '㌽' 13118 '㌾' 13120 '㍀' 13130 '㍊' 13131 '㍋' 13132 '㍌' 13134 '㍎' 13139 '㍓' 13140 '㍔' 13142 '㍖' .......... ﺋ' 65164 'ﺌ' 65269 'ﻵ' 65270 'ﻶ' 65271 'ﻷ' 65272 'ﻸ' 65273 'ﻹ' 65274'_
  • Es gibt einige Zeichen, die nicht normalisiert werden können , sie werfen ArgumentException wenn versucht
    _Total:2081_ Characters(int value): 55296-57343, 64976-65007, 65534

Diese Links können sehr hilfreich sein, um zu verstehen, welche Regeln für die Unicode-Äquivalenz gelten

  1. nicode-Äquivalenz
  2. nicode_compatibility_characters
24
dbw

Höchstwahrscheinlich gibt es zwei verschiedene Zeichencodes, die (sichtbar) dasselbe Zeichen ergeben. Obwohl technisch nicht gleich, sehen sie gleich aus. Sehen Sie sich die Zeichentabelle an und prüfen Sie, ob es mehrere Instanzen dieses Zeichens gibt. Oder drucken Sie den Zeichencode der beiden Zeichen in Ihrem Code aus.

9
PMF

Sie fragen "wie man sie vergleicht", aber Sie sagen uns nicht, was Sie tun möchten.

Es gibt mindestens zwei Möglichkeiten, sie zu vergleichen:

Entweder Sie vergleichen sie direkt wie Sie sind und sie sind unterschiedlich

Oder Sie verwenden die Unicode-Kompatibilitätsnormalisierung, wenn Sie einen Vergleich benötigen, bei dem die Übereinstimmungen gefunden werden.

Es könnte jedoch ein Problem geben, da durch die Normalisierung der Unicode-Kompatibilität viele andere Zeichen gleich sind. Wenn Sie möchten, dass nur diese beiden Zeichen gleich behandelt werden, sollten Sie Ihre eigenen Normalisierungs- oder Vergleichsfunktionen ausführen.

Für eine spezifischere Lösung müssen wir Ihr spezifisches Problem kennen. In welchem ​​Kontext sind Sie auf dieses Problem gestoßen?

6
hippietrail

Wenn ich pedantisch sein möchte, würde ich sagen, dass Ihre Frage keinen Sinn ergibt, aber da wir uns Weihnachten nähern und die Vögel singen, werde ich damit fortfahren.

Zunächst einmal sind die 2 Entitäten, die Sie vergleichen möchten, glyph s. Ein Glyphe ist Teil einer Reihe von Glyphen, die von einer "Schriftart" bereitgestellt werden, die normalerweise in einer ttf, otf oder welches Dateiformat Sie verwenden.

Die Glyphen stellen ein bestimmtes Symbol dar. Da es sich um eine Darstellung handelt, die von einer bestimmten Menge abhängt, können Sie nicht einfach davon ausgehen, dass zwei ähnliche oder sogar "bessere" identische Symbole vorhanden sind. Dies ist ein Satz, der keinen Sinn ergibt Wenn Sie den Kontext berücksichtigen, sollten Sie mindestens angeben, welche Schriftart oder welche Gruppe von Glyphen Sie berücksichtigen, wenn Sie eine Frage wie diese formulieren.

Was normalerweise verwendet wird, um ein ähnliches Problem wie das zu lösen, auf das Sie stoßen, ist eine OCR, im Wesentlichen eine Software, die Glyphen erkennt und vergleicht. Wenn C # ein OCR Standardmäßig weiß ich das nicht, aber es ist im Allgemeinen eine wirklich schlechte Idee, wenn Sie keine OCR benötigen und wissen, was Sie damit tun sollen.

Möglicherweise interpretieren Sie ein Physikbuch als ein altgriechisches Buch, ohne die Tatsache zu erwähnen, dass OCR im Allgemeinen ressourcenintensiv ist.

Es gibt einen Grund, warum diese Zeichen so lokalisiert sind, wie sie lokalisiert sind. Tun Sie das einfach nicht.

5
user2485710

Mit der DrawString -Methode ist es möglich, beide Zeichen mit demselben Schriftstil und derselben Schriftgröße zu zeichnen. Nachdem zwei Bitmaps mit Symbolen generiert wurden, können sie pixelweise verglichen werden.

Vorteil dieser Methode ist, dass Sie nicht nur absolut gleiche, sondern auch ähnliche Zeichen (mit definierter Toleranz) vergleichen können.

1
Ivan Kochurkin