我陷入了一个令人惊讶的问题。
我在我的应用程序中加载了一个文本文件,我有一些逻辑比较了μ的值。
我意识到即使文本相同,比较值也是错误的。
Console.WriteLine("μ".Equals("µ")); // returns false
Console.WriteLine("µ".Equals("µ")); // return true
在后面的行中,字符μ被复制粘贴。
但是,这些可能不是唯一的字符。
C#中是否有任何方法可以比较看起来相同但实际上不同的字符?
答案 0 :(得分:149)
因为它们实际上是不同的符号,即使它们看起来相同,首先是实际的字母,并且有char code = 956 (0x3BC)
,第二个是微型符号,并且181 (0xB5)
。
参考文献:
因此,如果您想要比较它们并且需要它们相等,则需要手动处理它,或者在比较之前将一个char替换为另一个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);
}
答案 1 :(得分:121)
在许多情况下,在比较它们之前,您可以normalize将两个Unicode字符转换为某个规范化形式,并且它们应该能够匹配。当然,您需要使用哪种规范化形式取决于角色本身;仅仅因为他们看起来并不一定意味着他们代表相同的角色。您还需要考虑它是否适合您的用例 - 请参阅Jukka K. Korpela的评论。
对于这种特殊情况,如果您参考Tony's answer中的链接,您会看到U+00B5的表格显示:
分解< compat> GREEK SMALL LETTER 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
和the Unicode spec。
答案 2 :(得分:86)
它们都有不同的字符代码: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
答案 3 :(得分:38)
对于μ
(mu)和µ
(微信号)的具体示例,后者对前者有compatibility decomposition,因此您可以normalize字符串到FormKC
或FormKD
将微观信号转换为mus。
但是,有许多字符集看起来很相似,但在任何Unicode规范化形式下都不相同。例如,A
(拉丁语),Α
(希腊语)和А
(西里尔语)。 Unicode网站有一个confusables.txt文件及其列表,旨在帮助开发人员防范homograph attacks。如有必要,您可以解析此文件并为字符串的“可视化规范化”构建一个表。
答案 4 :(得分:34)
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)
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)
答案 5 :(得分:24)
编辑此问题与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
________________________________________________________________
在阅读Unicode_equivalence中的信息时,我找到了
等价标准的选择会影响搜索结果。例如一些排版连字,如U + FB03(ffi),.....所以搜索为U + 0066(f)作为子串将成功在 NFKC U + FB03的归一化,但在U + FB03的 NFC 归一化中没有归一化。
为了比较等价,我们通常应该使用 FormKC
,即NFKC标准化或 FormKD
,即NFKD标准化。
我很想知道更多关于所有Unicode字符的知识,所以我制作了样本,它会迭代UTF-16
中的所有Unicode字符,我得到了一些我想要讨论的结果
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等价的规则非常有帮助
答案 6 :(得分:9)
最有可能的是,有两个不同的字符代码可以(明显地)生成相同的字符。虽然技术上并不相同,但看起来并不相同。查看字符表并查看该字符是否有多个实例。或者在代码中打印出两个字符的字符代码。
答案 7 :(得分:6)
你问“如何比较它们”,但你没有告诉我们你想做什么。
至少有两种主要方法可以比较它们:
要么直接比较它们,要么它们是不同的
如果您需要进行比较以查找匹配项,则使用Unicode兼容性规范化。
可能存在问题,因为Unicode兼容性规范化会使许多其他字符比较相等。如果您只想将这两个字符视为相似,则应该使用自己的规范化或比较函数。
对于更具体的解决方案,我们需要了解您的具体问题。你遇到这个问题的背景是什么?
答案 8 :(得分:5)
如果我想迂腐,我会说你的问题没有意义,但由于我们正在接近圣诞节并且鸟儿正在唱歌,我会继续这样做。
首先,您要比较的2个实体是glyph
s,字形是由通常称为“字体”提供的一组字形的一部分,通常是ttf
,otf
或您正在使用的任何文件格式。
字形是给定符号的表示,并且由于它们是依赖于特定集合的表示,因此您不能期望具有2个相似或甚至“更好”的相同符号,这是一个不具有的短语如果你考虑上下文有意义,你至少应该在你提出这样的问题时指定你正在考虑的字体或字体集。
通常用于解决与您遇到的问题类似的问题,它是一个OCR,本质上是识别和比较字形的软件,如果C#默认提供OCR我不知道,但如果你真的不需要OCR并且你知道该如何处理它通常是一个非常糟糕的主意。
你最终可能会将一本物理书籍解读为一本古希腊书籍而不提及OCR在资源方面通常很昂贵的事实。
有一个原因可以解释为什么这些字符的本地化方式是本地化的,只是不这样做。
答案 9 :(得分:1)
可以使用DrawString
方法绘制具有相同字体样式和大小的两个字符。生成两个带符号的位图后,可以逐个像素地比较它们。
这种方法的优点是你不仅可以比较绝对相等的字符,而且可以比较相似(具有明确的容差)。