如何比较“相似”的Unicode字符?

问题描述 投票:94回答:10

我陷入一个令人惊讶的问题。

我在应用程序中加载了一个文本文件,并且有一些逻辑比较具有µ的值。

而且我意识到即使文本相同,比较值也是错误的。

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

在后面的行中复制字符µ。

但是,可能不是唯一这样的字符。

C#中有什么方法可以比较看起来相同但实际上不同的字符?

c# .net string unicode string-comparison
10个回答
124
投票

[在很多情况下,您可以将两个Unicode字符normalize转换为某种规范化形式,然后再进行比较,它们应该能够匹配。当然,您需要使用哪种规范化形式取决于字符本身。仅仅因为它们look相似并不一定意味着它们表示相同的字符。您还需要考虑它是否适合您的用例-请参阅Jukka K. Korpela的评论。

在这种情况下,如果您参考Tony's answer中的链接,您会看到U+00B5的表显示:

分解希腊小写字母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.NormalizationFormSystem.Text.NormalizationForm


1
投票

可以使用otf方法以相同的字体样式和大小绘制两个字符。生成带有符号的两个位图后,可以逐像素比较它们。


0
投票

“μ”(U + 03BC和)对“μ”(U + 00B5)不是大问题,可以使用兼容的归一化解决。但是“ǝ”(U + 01DD),“ə”(U + 259)和“ә”(U + 4D9)是另一种情况。看起来无法比较这些字符,而它们的外观完全相同。


149
投票

因为即使它们看起来相同,也确实是不同的符号,第一个是实际字母并具有字符the Unicode spec,第二个是微符号并具有code = 956 (0x3BC)

参考:

因此,如果要比较它们并且需要它们相等,则需要手动处理它,或者在比较之前将一个字符替换为另一个字符。或使用以下代码:

Unicode Character 'MICRO SIGN' (U+00B5)

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


86
投票

它们都有不同的字符代码:Demo

Refer this for more details

第一个是:

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

“


39
投票

对于Display Friendly Code Decimal Code Hex Code Description ==================================================================== μ μ μ μ Lowercase Mu µ µ µ µ micro sign Mu (μ)和μ(微符号)的特定示例,后者具有前者的µ,因此您可以将字符串compatibility decomposition设为normalizeFormKC以将微信号转换为mus。

但是,在任何Unicode规范化形式下,都有许多相似的字符集,但并不等同。例如,FormKD(拉丁语),A(希腊语)和Α(西里尔字母)。 Unicode网站有一个А文件,其中包含这些文件的列表,旨在帮助开发人员防范confusables.txt。如有必要,您可以解析此文件并构建一个表以对字符串进行“可视化标准化”。


34
投票

[homograph attacks两个字符都在Search中,请参见差异

一个是Unicode database Greek small Letter,另一个是µ 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)

24
投票

EDIT该问题与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) 合并后原始答案发布:

How to compare 'μ' and 'µ' in C#

编辑在读完注释之后,是的,使用上述方法不好,因为它可能为其他类型的输入提供错误的结果,为此,我们应该使用 "μ".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 ________________________________________________________________ 中读取信息时,>

等价标准的选择会影响搜索结果。例如,某些印刷连字,例如U + FB03(ffi),...因此,对于U + 0066(f)作为子字符串的search

在U的NFKC归一化中将成功 + FB03,但不在U + FB03的NFC归一化中。

因此,为了比较等效性,我们通常应使用Unicode_equivalence

即NFKC归一化或FormKC即NFKD归一化。我不太想知道更多关于所有Unicode字符的信息,所以我制作了一个示例,该示例将遍历FormKD中的所有Unicode字符,并且得到了一些我想讨论的结果
  • 关于UTF-16FormC归一化值不相等的字符的信息FormDTotal: 12,118
  • 关于Character (int value): 192-197, 199-207, 209-214, 217-221, 224-253, ..... 44032-55203FormKC归一化值不相等的字符的信息FormKDTotal: 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-65274FormC归一化值不相等的所有字符,除了这些字符之外,FormDFormKC归一化值也不相等字符:FormKD901 '΅', 8129 '῁', 8141 '῍', 8142 '῎', 8143 '῏', 8157 '῝', 8158 '῞'
  • , 8159 '῟', 8173 '῭', 8174 '΅'FormKC归一化值不相等,但FormKDFormC归一化值相等的多余字符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'ArgumentExceptionTotal:2081

    此链接对于了解哪些规则适用于Unicode等价确实很有帮助

  1. Characters(int value): 55296-57343, 64976-65007, 65534
  2. Unicode_equivalence

9
投票

[最有可能的是,有两种不同的字符代码(明显地)构成相同的字符。尽管从技术上讲不相等,但它们看起来是相等的。查看字符表,看看该字符是否有多个实例。或在代码中打印出两个字符的字符代码。


6
投票

您问“如何比较它们”,但没有告诉我们您想做什么。


5
投票

[如果我想做个书呆子,我想说的是你的问题没有道理,但是由于我们临近圣诞节,而鸟儿也在唱歌,因此我将继续进行。

© www.soinside.com 2019 - 2024. All rights reserved.