Как сравнить символы Unicode, которые "похожи друг на друга"?

Я попадаю в удивительную проблему.

Я загрузил текстовый файл в свое приложение, и у меня есть некоторая логика, которая сравнивает значение с μ.

И я понял, что даже если тексты одинаковы, значение сравнения равно false.

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

В более поздней строке символ μ скопирован.

Однако это могут быть не только символы, похожие на это.

Есть ли способ в С# сравнивать символы, которые выглядят одинаково, но на самом деле разные?

Ответ 1

Во многих случаях вы можете нормализовать оба символа Unicode до определенной формы нормализации перед их сравнением, и они должны быть в состоянии соответствовать. Конечно, какую форму нормализации вам нужно использовать, зависит от самих персонажей; просто потому, что они выглядят одинаково, не обязательно означает, что они представляют один и тот же персонаж. Вам также нужно подумать, подходит ли оно для вашего случая использования - см. комментарий Юкки К. Корпела.

В этой конкретной ситуации, если вы перейдете по ссылкам в ответе Тони, вы увидите, что в таблице для U + 00B5 указано:

Разложение & lt; compat & gt; ГРЕЧЕСКОЕ МАЛЕНЬКОЕ ПИСЬМО MU (U + 03BC)

Это означает, что U + 00B5, второй символ в исходном сравнении, можно разложить на U + 03BC, первый символ.

Таким образом, вы нормализуете символы, используя полную декомпозицию совместимости с формами нормализации KC или KD. Вот небольшой пример, который я написал для демонстрации:

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

Подробнее о нормализации Unicode и различных формах нормализации см. System.Text.NormalizationForm и спецификацию Unicode.

Ответ 2

Поскольку это действительно разные символы, даже они выглядят одинаково, сначала это фактическая буква и char code = 956 (0x3BC), а вторая - микро-знак и имеет 181 (0xB5).

Литература:

Итак, если вы хотите сравнить их, и вам нужно, чтобы они были равными, вам нужно обработать их вручную или заменить один char на другой перед сравнением. Или используйте следующий код:

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

И Демо

Ответ 3

Оба они имеют разные коды символов: Обратитесь к этому для более подробной информации

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

Где, первый:

Display     Friendly Code   Decimal Code    Hex Code    Description
====================================================================
μ           μ            μ          μ     Lowercase Mu
µ           µ         µ          µ      micro sign Mu

Image

Ответ 4

Для конкретного примера μ (mu) и µ (микрознак) последний имеет декомпозицию совместимости к первому, так что вы можете нормализовать строку до FormKC или FormKD, чтобы преобразовать микро знаки в mus.

Однако существует множество наборов символов, которые похожи друг на друга, но не эквивалентны в любой форме нормализации Unicode. Например, A (латинский), Α (греческий) и А (кириллица). На веб-сайте Unicode есть confusables.txt файл со списком, который призван помочь разработчикам защищать от атак с использованием гомографа. При необходимости вы можете проанализировать этот файл и построить таблицу для "визуальной нормализации" строк.

Ответ 5

Search оба символа в базе данных Unicode и см. разницу .

Один - это греческое небольшое письмо µ, а другое - 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)

Ответ 6

EDIT После слияния этого вопроса с Как сравнить "μ" и "μ" в С#
Оригинальный ответ отправлен:

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

ИЗМЕНИТЬ После прочтения комментариев, да, нехорошо использовать вышеупомянутый метод, потому что он может давать неправильные результаты для некоторых других типов входов, для этого мы должны использовать normalize с использованием полной декомпозиции совместимости, как указано в wiki. (Спасибо, ответили 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();
    }

Выход

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
 ________________________________________________________________

При чтении информации в Unicode_equivalence я нашел

Выбор критериев эквивалентности может повлиять на результаты поиска. Например, некоторые типографские лигатуры, такие как U + FB03 (ffi),..... поэтому поиск для U + 0066 (f) в качестве подстроки преуспеть в NFKC нормализация U + FB03, но не в NFC нормализация U + FB03.

Итак, для сравнения эквивалентности мы обычно должны использовать FormKC, то есть нормализация NFKC или FormKD, то есть нормализация NFKD.
Мне было любопытно узнать больше обо всех символах Юникода, поэтому я сделал образец, который будет перебирать весь символ Юникода в UTF-16, и я получил некоторые результаты, которые хочу обсудить

  • Информация о символах, чьи нормальные значения FormC и FormD не были эквивалентными
    Total: 12,118
    Character (int value): 192-197, 199-207, 209-214, 217-221, 224-253, ..... 44032-55203
  • Информация о символах, чьи нормальные значения FormKC и FormKD не были эквивалентными
    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
  • Все символы, чье нормальное значение FormC и FormD не были эквивалентными, там FormKC и FormKD нормализованные значения также не были эквивалентны, кроме этих символов
    Персонажи: 901 '΅', 8129 '῁', 8141 '῍', 8142 '῎', 8143 '῏', 8157 '῝', 8158 '῞'
    , 8159 '῟', 8173 '῭', 8174 '΅'
  • Дополнительный символ, чье нормальное значение FormKC и FormKD не было эквивалентным, но там FormC и FormD нормализованные значения были эквивалентны Total: 119
    Символы: 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'
  • Есть некоторые символы, которые не могут быть нормализованы, они бросают ArgumentException, если их пробовали Total:2081 Characters(int value): 55296-57343, 64976-65007, 65534

Эти ссылки могут быть действительно полезны для понимания того, какие правила действуют для эквивалентности Unicode.

Ответ 7

Скорее всего, есть два разных символьных кода, которые делают (видимо) один и тот же символ. Хотя технически они не равны, они выглядят одинаково. Посмотрите на таблицу символов и посмотрите, есть ли несколько экземпляров этого символа. Или распечатайте код символа двух символов в коде.

Ответ 8

Вы спрашиваете "как их сравнить", но вы не говорите нам, что вы хотите сделать.

Существует как минимум два основных способа их сравнения:

Либо вы сравниваете их напрямую, как и вы, и они разные

Или вы используете нормализацию совместимости Юникода, если ваша потребность заключается в сравнении, которое их сопоставляет.

Может возникнуть проблема, хотя из-за того, что нормализация совместимости Unicode заставит многие другие символы сравняться. Если вы хотите, чтобы только эти два символа считались одинаковыми, вы должны перевернуть свои собственные функции нормализации или сравнения.

Для более конкретного решения нам нужно знать вашу конкретную проблему. Каков контекст, под которым вы столкнулись с этой проблемой?

Ответ 9

Если бы я хотел быть педантичным, я бы сказал, что ваш вопрос не имеет смысла, но поскольку мы приближаемся к Рождеству, и птицы поют, я продолжу это.

Во-первых, 2 сущности, которые вы пытаетесь сравнить, - это glyph s, глиф - часть набора символов, предоставляемых тем, что обычно известно как "шрифт", вещь, обычно встречающаяся в ttf, otf или любой другой формат файла, который вы используете.

Глифы представляют собой представление данного символа, и поскольку они представляют собой представление, зависящее от определенного набора, вы не можете просто ожидать, что у вас есть два одинаковых или даже "лучших" одинаковых символа, это фраза, Если вы рассматриваете контекст, вы должны хотя бы указать, какой шрифт или набор глифов вы рассматриваете, когда формулируете такой вопрос.

Что обычно используется для решения проблемы, аналогичной той, с которой вы сталкиваетесь, это OCR, по существу программное обеспечение, которое распознает и сравнивает глифы. Если С# предоставляет OCR по умолчанию я этого не знаю, но, как правило, это действительно плохая идея, если вам действительно не нужно OCR, и вы знаете, что с ним делать.

Вы можете в конечном итоге интерпретировать книгу физики как древнюю греческую книгу, не упоминая тот факт, что OCR, как правило, дороги с точки зрения ресурсов.

Есть причина, по которой эти символы локализованы так, как они локализованы, просто не делайте этого.

Ответ 10

Можно рисовать оба символа с одинаковым стилем и размером шрифта с помощью метода DrawString. После того, как были созданы два растровых изображения с символами, можно сравнить их по пикселям.

Преимущество этого метода заключается в том, что вы можете сравнивать не только абсолютные равные символы, но и аналогичные (с определенным допуском).