我陷入一个令人惊讶的问题。
我在应用程序中加载了一个文本文件,并且有一些逻辑比较具有µ的值。
我意识到即使文本相同,比较值也是错误的。
Console.WriteLine("μ".Equals("µ")); // returns false
Console.WriteLine("µ".Equals("µ")); // return true
在后面的行中,字符µ被复制粘贴。
但是,这些可能不是唯一的类似字符。
C#中有什么方法可以比较看起来相同但实际上不同的字符?
我陷入一个令人惊讶的问题。
我在应用程序中加载了一个文本文件,并且有一些逻辑比较具有µ的值。
我意识到即使文本相同,比较值也是错误的。
Console.WriteLine("μ".Equals("µ")); // returns false
Console.WriteLine("µ".Equals("µ")); // return true
在后面的行中,字符µ被复制粘贴。
但是,这些可能不是唯一的类似字符。
C#中有什么方法可以比较看起来相同但实际上不同的字符?
Answers:
在很多情况下,你可以正常化比较之前两个Unicode字符的一定范式,他们应该能够匹配。当然,您需要使用哪种规范化形式取决于字符本身。只是因为他们看起来相似并不一定意味着它们代表相同的角色。您还需要考虑它是否适合您的用例-请参阅Jukka K. Korpela的评论。
对于这种特殊情况,如果您参考Tony的答案中的链接,则会看到U + 00B5的表显示:
分解<compat>希腊小写字母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规范。
因为即使它们看起来相同,也确实是不同的符号,第一个是实际字母并具有char code = 956 (0x3BC)
,第二个是微符号并具有181 (0xB5)
。
参考文献:
因此,如果要比较它们并且需要它们相等,则需要手动处理它,或者在比较之前将一个字符替换为另一个字符。或使用以下代码:
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);
}
和演示
它们都有不同的字符代码:有关更多详细信息,请参考此代码
Console.WriteLine((int)'μ'); //956
Console.WriteLine((int)'µ'); //181
其中,第一个是:
Display Friendly Code Decimal Code Hex Code Description
====================================================================
μ μ μ μ Lowercase Mu
µ µ µ µ micro sign Mu
对于μ
(mu)和µ
(micro sign)的特定示例,后者具有与前者的兼容性分解,因此您可以将字符串标准化为FormKC
或FormKD
将微符号转换为mus。
但是,有许多相似的字符集,但是在任何Unicode规范化形式下都不相同。例如,A
(拉丁),Α
(希腊)和А
(西里尔字母)。Unicode网站上有一个confusables.txt文件,其中包含这些文件的列表,目的是帮助开发人员防御同形异义词攻击。如有必要,您可以解析此文件并构建一个表以对字符串进行“可视化标准化”。
ToUpper
/ ToLower
难以实施。你需要有"B".ToLower()
可以b
在英语,但β
在希腊和в
俄罗斯。实际上,只有土耳其语(dotless i
)和其他几种语言需要的大小写规则与默认规则不同。
在Unicode数据库中搜索两个字符,然后看一下区别。
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)
编辑这个问题与如何在C#中比较'μ'和'µ'合并后,
原始答案发布了:
"μ".ToUpper().Equals("µ".ToUpper()); //This always return true.
编辑 阅读评论后,是的,使用上述方法不好,因为它可能为某些其他类型的输入提供错误的结果,为此,我们应使用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归一化。
我不太想知道更多关于所有Unicode字符的信息,所以我制作了一个示例,该示例将遍历所有Unicode字符,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等效非常有用。
"m".ToUpper().Equals("µ".ToUpper());
,"M".ToUpper().Equals("µ".ToUpper());
并且也是正确的。这可能不是理想的。
您问“如何比较它们”,但没有告诉我们您想做什么。
比较它们至少有两种主要方法:
您要么直接比较它们,要么就不同了
或者,如果需要进行比较以找到匹配的匹配项,则可以使用Unicode兼容性规范化。
但是可能存在问题,因为Unicode兼容性规范化将使许多其他字符比较相等。如果只希望将这两个字符视为相同,则应滚动自己的规范化或比较函数。
对于更具体的解决方案,我们需要了解您的具体问题。您遇到此问题的背景是什么?
如果我想做个书呆子,我想说您的问题没有道理,但是由于我们临近圣诞节,而鸟儿也在唱歌,因此我将继续进行下去。
首先,您要比较的2个实体是glyph
s,一个字形是通常由“字体”提供的一组字形的一部分,通常以来提供ttf
,otf
或者是您使用的任何文件格式使用。
字形是给定符号的表示形式,并且由于它们是取决于特定集合的表示形式,因此您不能仅期望拥有2个相似甚至“更好”的相同符号,这是一个没有意义的短语如果考虑到上下文,则在制定这样的问题时,至少应指定要考虑的字体或字形集。
通常用于解决与您遇到的问题类似的问题的工具,它是OCR,本质上是一种识别和比较字形的软件,如果C#提供了OCR,默认,我不知道,但是通常这是一个很糟糕的做法知道您是否真的不需要OCR,并且知道如何使用它。
您可能最终将一本物理学书籍解释为一本古希腊书籍,而没有提及OCR在资源方面通常很昂贵的事实。
这些字符按照其本地化方式进行本地化是有原因的,只是不要这样做。
可以使用DrawString
method 绘制具有相同字体样式和大小的两个字符。生成带有符号的两个位图后,可以逐像素比较它们。
这种方法的优势在于,您不仅可以比较绝对相等的字符,还可以比较相似的字符(具有确定的容差)。