2008-12-11 18 views
112

Necesito comparar 2 cadenas en C# y tratar letras acentuadas de la misma manera que las letras sin acentos. Por ejemplo:Ignorar letras acentuadas en la comparación de cadenas

string s1 = "hello"; 
string s2 = "héllo"; 

s1.Equals(s2, StringComparison.InvariantCultureIgnoreCase); 
s1.Equals(s2, StringComparison.OrdinalIgnoreCase); 

Estos 2 cadenas tienen que ser los mismos (por lo que mi solicitud se refiere), pero ambos de estos estados se evalúan como falso. ¿Hay alguna manera en C# para hacer esto?

Respuesta

211

EDITAR 2012-01-20: ¡Oh chico! La solución fue mucho más simple y ha estado en el marco casi para siempre. As pointed out by knightpfhor:

string.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace); 

Aquí es una función que elimina los 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 on MichKap's blog (RIP...).

El principio es que convierte 'é' en 2 caracteres sucesivos 'e', ​​agudos. Luego recorre los caracteres y omite los signos diacríticos.

"héllo" se convierte en "él <agudo> llo", que a su vez se convierte en "hola".

Debug.Assert("hello"==RemoveDiacritics("héllo")); 

Nota: He aquí una más compacto.NET4 versión + amistosa 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); 
} 
+6

Cada vez que quiero hacer eso, termino en su publicación y nunca volví a votar. Hay un buen señor. ¡Voto ascendente! –

+2

Impresionante. Muchas gracias. – Smur

+1

¿Cómo hacerlo en un núcleo .net ya que no tiene 'string.Normalize'? –

-3

intente esta sobrecarga en el método String.Compare.

String.Compare Método (String, String, Boolean, CultureInfo)

Produce un valor int basado en las operaciones de comparación incluyendo CultureInfo. el ejemplo en la página compara "Cambiar" en en-US y en-CZ. CH en en-CZ es una sola "letra".

ejemplo desde el 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 
*/ 

para ello por los idiomas acentuados tendrá que obtener la cultura, entonces probar las cuerdas en base a eso.

http://msdn.microsoft.com/en-us/library/hyxc48dt.aspx

+0

Este es un mejor enfoque que la comparación directa de las cadenas, pero aún considera la letra base y su versión acentuada * diferente *. Por lo tanto, no responde a la pregunta original, que quería que se ignoraran los acentos. –

6

El método siguiente CompareIgnoreAccents(...) trabaja en sus datos de ejemplo. Aquí está el artículo donde conseguí 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 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))); 
} 

A continuación, el uso sería la siguiente:

if(string.Compare(s1.RemoveAccents(), s2.RemoveAccents(), true) == 0) { 
    ... 
+1

esto hace una letra acentuada a '?' – onmyway133

+3

Esta es una comparación destructiva, donde por ejemplo ā y ē se tratarán como iguales. Pierdes cualquier carácter por encima de 0xFF y no hay garantía de que las cuerdas sean iguales, ignorando acentos. – Abel

+0

Pierdes también cosas como ñ. No es una solución si me preguntas. –

106

Si no es necesario para convertir la cadena y lo que desea para comprobar la igualdad puede utilizar

string s1 = "hello"; 
string s2 = "héllo"; 

if (String.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace) == 0) 
{ 
    // both strings are equal 
} 

o si desea que la comparación con también distinga entre 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 
} 
+3

¡Ojo de buey 100%! –

+0

Si alguien más siente curiosidad acerca de esta opción IgnoreNonSpace, le recomendamos que lea esta discusión al respecto. http://www.pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; está bien :) –

+0

en msdn: "El estándar Unicode define la combinación de caracteres como caracteres que se combinan con caracteres base para producir un nuevo carácter. Los caracteres de combinación no espaciados no ocupan una posición de separación por sí mismos cuando se procesan". – Avlin

0

Tuve que hacer algo similar pero con un método StartsWith. Aquí hay una solución simple derivada de @Serge - appTranslator.

Aquí es 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 los monstruos de trazadores de líneas uno;)

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; 
    } 

Accent incensitive y caso incensitive startsWith puede ser llamado como esto

value.ToString().StartsWith(str, CultureInfo.InvariantCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) 
0

A 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) 
Cuestiones relacionadas