我陷入一个令人惊讶的问题。
我在应用程序中加载了一个文本文件,并且有一些逻辑比较具有µ的值。
而且我意识到即使文本相同,比较值也是错误的。
Console.WriteLine("μ".Equals("µ")); // returns false
Console.WriteLine("µ".Equals("µ")); // return true
在后面的行中复制字符µ。
但是,可能不是唯一这样的字符。
C#中有什么方法可以比较看起来相同但实际上不同的字符?
[在很多情况下,您可以将两个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.NormalizationForm
和System.Text.NormalizationForm
。
可以使用otf
方法以相同的字体样式和大小绘制两个字符。生成带有符号的两个位图后,可以逐像素比较它们。
“μ”(U + 03BC和)对“μ”(U + 00B5)不是大问题,可以使用兼容的归一化解决。但是“ǝ”(U + 01DD),“ə”(U + 259)和“ә”(U + 4D9)是另一种情况。看起来无法比较这些字符,而它们的外观完全相同。
因为即使它们看起来相同,也确实是不同的符号,第一个是实际字母并具有字符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);
}
它们都有不同的字符代码:Demo
Refer this for more details
第一个是:
Console.WriteLine((int)'μ'); //956
Console.WriteLine((int)'µ'); //181
对于Display Friendly Code Decimal Code Hex Code Description
====================================================================
μ μ μ μ Lowercase Mu
µ µ µ µ micro sign Mu
(μ)和μ
(微符号)的特定示例,后者具有前者的µ
,因此您可以将字符串compatibility decomposition设为normalize或FormKC
以将微信号转换为mus。
但是,在任何Unicode规范化形式下,都有许多相似的字符集,但并不等同。例如,FormKD
(拉丁语),A
(希腊语)和Α
(西里尔字母)。 Unicode网站有一个А
文件,其中包含这些文件的列表,旨在帮助开发人员防范confusables.txt。如有必要,您可以解析此文件并构建一个表以对字符串进行“可视化标准化”。
[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)
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
________________________________________________________________
中读取信息时,>
即NFKC归一化或等价标准的选择会影响搜索结果。例如,某些印刷连字,例如U + FB03(ffi),...因此,对于U + 0066(f)作为子字符串的search
在U的NFKC归一化中将成功 + FB03,但不在U + FB03的NFC归一化中。因此,为了比较等效性,我们通常应使用Unicode_equivalence
FormKC
即NFKD归一化。我不太想知道更多关于所有Unicode字符的信息,所以我制作了一个示例,该示例将遍历FormKD
中的所有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
此链接对于了解哪些规则适用于Unicode等价确实很有帮助
Characters(int value): 55296-57343, 64976-65007, 65534
[最有可能的是,有两种不同的字符代码(明显地)构成相同的字符。尽管从技术上讲不相等,但它们看起来是相等的。查看字符表,看看该字符是否有多个实例。或在代码中打印出两个字符的字符代码。
您问“如何比较它们”,但没有告诉我们您想做什么。
[如果我想做个书呆子,我想说的是你的问题没有道理,但是由于我们临近圣诞节,而鸟儿也在唱歌,因此我将继续进行。