c# - strings - string.compare vb
¿Cómo comparar caracteres Unicode que "se parecen"? (10)
Caigo en un problema sorprendente.
Cargué un archivo de texto en mi aplicación y tengo una lógica que compara el valor que tiene μ.
Y me di cuenta de que incluso si los textos son iguales, el valor de comparación es falso.
Console.WriteLine("μ".Equals("µ")); // returns false
Console.WriteLine("µ".Equals("µ")); // return true
En la línea posterior, el carácter μ se copia pegado.
Sin embargo, estos pueden no ser los únicos personajes que son así.
¿Hay alguna forma en C # para comparar los personajes que se ven iguales pero que en realidad son diferentes?
Ambos tienen diferentes códigos de personaje: refiéranlo para más detalles
Console.WriteLine((int)''μ''); //956
Console.WriteLine((int)''µ''); //181
Donde, el primero es:
Display Friendly Code Decimal Code Hex Code Description
====================================================================
μ μ μ μ Lowercase Mu
µ µ µ µ micro sign Mu
Como en realidad son símbolos diferentes, aunque parezcan iguales, primero es la letra real y tiene el code = 956 (0x3BC)
y el segundo es el signo de micro y tiene 181 (0xB5)
.
Referencias
Por lo tanto, si desea compararlos y necesita que sean iguales, debe manejarlos manualmente o reemplazar uno por otro antes de realizar una comparación. O usa el siguiente código:
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);
}
Y la demo
En muchos casos, puede normalizar ambos caracteres Unicode a una cierta forma de normalización antes de compararlos, y ellos deberían poder hacer coincidir. Por supuesto, qué forma de normalización necesitas usar depende de los personajes mismos; solo porque se parezcan no significa necesariamente que representen el mismo personaje. También debe considerar si es apropiado para su caso de uso; consulte el comentario de Jukka K. Korpela.
Para esta situación particular, si se refiere a los enlaces en la respuesta de Tony , verá que la tabla para U + 00B5 dice:
Descomposición <compat> GRIETA GRIEGA MU (U + 03BC)
Esto significa que U + 00B5, el segundo personaje en su comparación original, puede descomponerse en U + 03BC, el primer personaje.
Por lo tanto, normalizará los caracteres utilizando la descomposición de compatibilidad completa, con las formas de normalización KC o KD. Aquí hay un ejemplo rápido que escribí para demostrar:
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
}
}
Para obtener más información sobre la normalización Unicode y las diferentes formas de normalización, consulte System.Text.NormalizationForm
y la especificación Unicode .
Es posible dibujar ambos caracteres con el mismo estilo y tamaño de fuente con DrawString
método DrawString
. Después de que se hayan generado dos mapas de bits con símbolos, es posible compararlos píxel por píxel.
La ventaja de este método es que puede comparar no solo absoluto charcters iguales, sino también similares (con tolerancia definida).
Lo más probable es que haya dos códigos de caracteres diferentes que hacen (visiblemente) el mismo personaje. Aunque técnicamente no son iguales, se ven iguales. Eche un vistazo a la tabla de caracteres y vea si hay múltiples instancias de ese personaje. O imprima el código de carácter de los dos caracteres en su código.
Para el ejemplo específico de μ
(mu) y µ
(micro-señal), este último tiene una descomposición de compatibilidad con el anterior, por lo que puede normalizar la cadena a FormKC
o FormKD
para convertir los FormKD
en mus.
Sin embargo, hay muchos conjuntos de caracteres que se parecen pero que no son equivalentes en ninguna forma de normalización Unicode. Por ejemplo, A
(latín), Α
(griego) y А
(cirílico). El sitio web de Unicode tiene un archivo confusables.txt con una lista de estos, destinado a ayudar a los desarrolladores a protegerse contra los ataques homógrafos . Si es necesario, puede analizar este archivo y crear una tabla para la "normalización visual" de las cadenas.
Si quisiera ser pedante, diría que su pregunta no tiene sentido, pero como nos acercamos a la Navidad y los pájaros cantan, procederé con esto.
En primer lugar, las 2 entidades que está tratando de comparar son glyph
, un glifo es parte de un conjunto de glifos proporcionados por lo que generalmente se conoce como "fuente", lo que generalmente viene en un ttf
, otf
o cualquier archivo formato que está utilizando.
Los glifos son una representación de un símbolo dado, y dado que son una representación que depende de un conjunto específico, no se puede esperar tener 2 símbolos idénticos o incluso "mejores" idénticos, es una frase que no tiene sentido si considera el contexto, al menos debe especificar qué fuente o conjunto de glifos está considerando al formular una pregunta como esta.
Lo que generalmente se usa para resolver un problema similar al que está encontrando, es un OCR, esencialmente un software que reconoce y compara glifos. Si C # proporciona un OCR por defecto, no lo sé, pero generalmente es muy malo idea si realmente no necesita un OCR y sabe qué hacer con él.
Posiblemente pueda terminar interpretando un libro de física como un antiguo libro griego sin mencionar el hecho de que los OCR son generalmente caros en términos de recursos.
Hay una razón por la cual esos caracteres están localizados de la manera en que están localizados, simplemente no hagas eso.
Usted pregunta "cómo compararlos" pero no nos dice lo que quiere hacer.
Hay al menos dos formas principales de compararlos:
O los comparas directamente como eres y son diferentes
O usa la Normalización de compatibilidad Unicode si lo necesita para una comparación que los encuentre coincidentes.
Sin embargo, podría haber un problema porque la normalización de compatibilidad Unicode hará que muchos otros personajes se igualen. Si desea que solo se trate a estos dos personajes como iguales, debe desplegar sus propias funciones de normalización o comparación.
Para una solución más específica, necesitamos conocer su problema específico. ¿Cuál es el contexto en el que se encontró con este problema?
EDITAR Después de la fusión de esta pregunta con Cómo comparar ''μ'' y ''μ'' en C #
Respuesta original publicada:
"μ".ToUpper().Equals("µ".ToUpper()); //This always return true.
EDITAR Después de leer los comentarios, sí, no es bueno usar el método anterior porque puede proporcionar resultados incorrectos para otro tipo de entradas, para esto debemos usar normalizar usando descomposición de compatibilidad completa como se menciona en wiki . (Gracias a la respuesta publicada por 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();
}
Salida
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
________________________________________________________________
Mientras leía información en Unicode_equivalence que encontré
La elección de los criterios de equivalencia puede afectar los resultados de búsqueda. Por ejemplo, algunas ligaduras tipográficas como U + FB03 (ffi), ..... entonces una búsqueda de U + 0066 (f) como subcadena tendría éxito en una normalización de NFKC de U + FB03 pero no en la normalización de NFC de U + FB03.
Por lo tanto, para comparar la equivalencia, normalmente deberíamos utilizar FormKC
es decir, la normalización de FormKD
o FormKD
es decir, la normalización de NFKD.
Estaba un poco curioso de saber más sobre todos los caracteres Unicode, así que hice una muestra que iteraría sobre todo el carácter Unicode en UTF-16
y obtuve algunos resultados de los que quiero hablar
- Información sobre los caracteres cuyos valores normalizados
FormC
yFormD
no son equivalentes
Total: 12,118
Character (int value): 192-197, 199-207, 209-214, 217-221, 224-253, ..... 44032-55203
- Información sobre los caracteres cuyos valores normalizados de
FormKC
yFormKD
no eran equivalentes
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
- Todos los caracteres cuyo valor normalizado
FormC
yFormD
no eran equivalentes, los valores normalizados deFormKC
yFormKD
tampoco eran equivalentes, excepto estos caracteres
Caracteres:901 ''΅'', 8129 ''῁'', 8141 ''῍'', 8142 ''῎'', 8143 ''῏'', 8157 ''῝'', 8158 ''῞''
, 8159 ''῟'', 8173 ''῭'', 8174 ''΅''
- Carácter adicional cuyo valor normalizado de
FormKC
yFormKD
no era equivalente, pero los valores normalizados deFormC
yFormD
eran equivalentes
Total: 119
Personajes: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''
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''
- Hay algunos personajes que no pueden ser normalizados , lanzan
ArgumentException
si lo intentan
Total:2081
Characters(int value): 55296-57343, 64976-65007, 65534
Estos enlaces pueden ser realmente útiles para comprender qué reglas rigen para la equivalencia Unicode
Busque ambos caracteres en una base de datos Unicode y vea la diferencia .
Una es la Letra pequeña griega µ
y la otra es la Micro Señal µ
.
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)