c# - tildes - Ignorar letras acentuadas en la comparación de cadenas
reemplazar letras con acento javascript (6)
Necesito comparar 2 cadenas en C # y tratar letras acentuadas de la misma manera que las letras sin acentos. Por ejemplo:
string s1 = "hello";
string s2 = "héllo";
s1.Equals(s2, StringComparison.InvariantCultureIgnoreCase);
s1.Equals(s2, StringComparison.OrdinalIgnoreCase);
Estas 2 cadenas deben ser iguales (en lo que respecta a mi aplicación), pero ambas declaraciones se evalúan como falsas. ¿Hay alguna manera en C # para hacer esto?
EDITAR 2012-01-20: ¡Oh chico! La solución fue mucho más simple y ha estado en el marco casi para siempre. Como lo señaló knightpfhor :
string.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace);
Aquí hay una función que elimina signos diacríticos de una cadena:
static string RemoveDiacritics(string text)
{
string formD = text.Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
foreach (char ch in formD)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(ch);
if (uc != UnicodeCategory.NonSpacingMark)
{
sb.Append(ch);
}
}
return sb.ToString().Normalize(NormalizationForm.FormC);
}
Más detalles sobre el blog de MichKap ( RIP... ).
El principio es que se convierte ''é'' en 2 caracteres sucesivos ''e'', agudo. A continuación, recorre los caracteres y omite los signos diacríticos.
"héllo" se convierte en "el <agudo> llo", que a su vez se convierte en "hola".
Debug.Assert("hello"==RemoveDiacritics("héllo"));
Nota: Aquí hay una versión más compacta .NET4 + amigable de la misma función:
static string RemoveDiacritics(string text)
{
return string.Concat(
text.Normalize(NormalizationForm.FormD)
.Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch)!=
UnicodeCategory.NonSpacingMark)
).Normalize(NormalizationForm.FormC);
}
El siguiente método CompareIgnoreAccents(...)
funciona en sus datos de ejemplo. Aquí está el artículo donde obtuve mi información de fondo: http://www.codeproject.com/KB/cs/EncodingAccents.aspx
private static bool CompareIgnoreAccents(string s1, string s2)
{
return string.Compare(
RemoveAccents(s1), RemoveAccents(s2), StringComparison.InvariantCultureIgnoreCase) == 0;
}
private static string RemoveAccents(string s)
{
Encoding destEncoding = Encoding.GetEncoding("iso-8859-8");
return destEncoding.GetString(
Encoding.Convert(Encoding.UTF8, destEncoding, Encoding.UTF8.GetBytes(s)));
}
Creo que un método de extensión sería mejor:
public static string RemoveAccents(this string s)
{
Encoding destEncoding = Encoding.GetEncoding("iso-8859-8");
return destEncoding.GetString(
Encoding.Convert(Encoding.UTF8, destEncoding, Encoding.UTF8.GetBytes(s)));
}
Entonces el uso sería este:
if(string.Compare(s1.RemoveAccents(), s2.RemoveAccents(), true) == 0) {
...
Si no necesita convertir la cadena y solo desea verificar la igualdad, puede usar
string s1 = "hello";
string s2 = "héllo";
if (String.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace) == 0)
{
// both strings are equal
}
o si quieres que la comparación también sea insensible a las mayúsculas y minúsculas
string s1 = "HEllO";
string s2 = "héLLo";
if (String.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) == 0)
{
// both strings are equal
}
Tenía que hacer algo similar, pero con un método StartsWith. Aquí hay una solución simple derivada de @Serge - appTranslator.
Aquí hay un método de extensión:
public static bool StartsWith(this string str, string value, CultureInfo culture, CompareOptions options)
{
if (str.Length >= value.Length)
return string.Compare(str.Substring(0, value.Length), value, culture, options) == 0;
else
return false;
}
Y para uno fanáticos de liners;)
public static bool StartsWith(this string str, string value, CultureInfo culture, CompareOptions options)
{
return str.Length >= value.Length && string.Compare(str.Substring(0, value.Length), value, culture, options) == 0;
}
Acento incensitivo y inicio de caso incensitivoCon este se puede llamar así
value.ToString().StartsWith(str, CultureInfo.InvariantCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase)
Una forma más simple de eliminar acentos:
Dim source As String = "áéíóúç"
Dim result As String
Dim bytes As Byte() = Encoding.GetEncoding("Cyrillic").GetBytes(source)
result = Encoding.ASCII.GetString(bytes)
prueba esta sobrecarga en el método String.Compare.
Método String.Compare (String, String, Boolean, CultureInfo)
Produce un valor int basado en las operaciones de comparación, incluido cultureinfo. el ejemplo en la página compara "Cambiar" en en-US y en-CZ. CH en en-CZ es una sola "letra".
ejemplo del enlace
using System;
using System.Globalization;
class Sample {
public static void Main() {
String str1 = "change";
String str2 = "dollar";
String relation = null;
relation = symbol( String.Compare(str1, str2, false, new CultureInfo("en-US")) );
Console.WriteLine("For en-US: {0} {1} {2}", str1, relation, str2);
relation = symbol( String.Compare(str1, str2, false, new CultureInfo("cs-CZ")) );
Console.WriteLine("For cs-CZ: {0} {1} {2}", str1, relation, str2);
}
private static String symbol(int r) {
String s = "=";
if (r < 0) s = "<";
else if (r > 0) s = ">";
return s;
}
}
/*
This example produces the following results.
For en-US: change < dollar
For cs-CZ: change > dollar
*/
por lo tanto, para los idiomas acentuados tendrá que obtener la cultura y luego probar las cadenas en función de eso.