Ignorer les lettres accentuées dans la comparaison de chaînes

141

Je dois comparer 2 chaînes en C # et traiter les lettres accentuées de la même manière que les lettres non accentuées. Par exemple:

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

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

Ces 2 chaînes doivent être identiques (en ce qui concerne mon application), mais ces deux déclarations donnent la valeur false. Existe-t-il un moyen en C # de faire cela?

Jon Tackabury
la source

Réponses:

251

EDIT 2012-01-20: Oh garçon! La solution était tellement plus simple et a été dans le cadre presque pour toujours. Comme l'a souligné knightpfhor :

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

Voici une fonction qui supprime les signes diacritiques d'une chaîne:

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

Plus de détails sur le blog de MichKap ( RIP ... ).

Le principe est qu'il transforme 'é' en 2 caractères successifs 'e', ​​aigus. Il parcourt ensuite les caractères et ignore les signes diacritiques.

"héllo" devient "il <acute> llo", qui à son tour devient "bonjour".

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

Remarque: Voici une version plus compacte et conviviale .NET4 + de la même fonction:

static string RemoveDiacritics(string text)
{
  return string.Concat( 
      text.Normalize(NormalizationForm.FormD)
      .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch)!=
                                    UnicodeCategory.NonSpacingMark)
    ).Normalize(NormalizationForm.FormC);
}
Serge Wautier
la source
1
Comment le faire en .net core car il ne l'a pas string.Normalize?
Andre Soares
Merci pour cela, j'aimerais pouvoir voter plus d'une fois! Cependant, il ne gère pas toutes les lettres accentuées, par exemple ð, ħ et ø ne sont pas convertis respectivement en o, h et o. Y a-t-il un moyen de les gérer également?
Avrohom Yisroel
@AvrohomYisroel le "ð" est une "lettre minuscule latine Eth", qui est une lettre séparée, pas un "o-avec-accent" ou "d-avec-accent". Les autres sont "Lettre minuscule latine H avec trait" et "Lettre minuscule latine O avec trait" qui peuvent également être considérés comme des lettres séparées
Hans Ke st ing
135

Si vous n'avez pas besoin de convertir la chaîne et que vous souhaitez simplement vérifier l'égalité, vous pouvez utiliser

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

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

ou si vous voulez que la comparaison soit également insensible à la casse

string s1 = "HEllO";
string s2 = "héLLo";

if (String.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) == 0)
{
    // both strings are equal
}
chevalierpfhor
la source
Si quelqu'un d'autre est curieux de connaître cette option IgnoreNonSpace, vous voudrez peut-être lire cette discussion à ce sujet. pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; c'est ok :)
Jim W dit de réintégrer Monica
on msdn: "La norme Unicode définit les caractères de combinaison comme des caractères qui sont combinés avec des caractères de base pour produire un nouveau caractère. Les caractères de combinaison sans espacement n'occupent pas une position d'espacement par eux-mêmes lorsqu'ils sont rendus."
Avlin
ok cette méthode a échoué pour ces 2 chaînes: tarafli / TARAFLİ cependant le serveur SQL dit égal comme supposé être
MonsterMMORPG
2
En effet, SQL Server est généralement configuré pour ne pas respecter la casse, mais par défaut, les comparaisons dans .Net sont sensibles à la casse. J'ai mis à jour la réponse pour montrer comment rendre cette casse insensible.
knightpfhor
J'essaye de créer un IEqualityComparer. Il doit fournir GetHashCode ... Comment l'obtenir (il doit être le même s'il est égal)
Yepeekai
5

La méthode suivante CompareIgnoreAccents(...)fonctionne sur vos données d'exemple. Voici l'article où j'ai obtenu mes informations générales: 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)));
}

Je pense qu'une méthode d'extension serait meilleure:

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

Alors l'utilisation serait la suivante:

if(string.Compare(s1.RemoveAccents(), s2.RemoveAccents(), true) == 0) {
   ...
Ryan Cook
la source
1
cela fait une lettre accentuée à '?'
onmyway133
4
Il s'agit d'une comparaison destructrice, où par exemple â et ē seront traités comme égaux. Vous perdez tous les caractères au-dessus de 0xFF et il n'y a aucune garantie que les chaînes soient des accents égaux ignorants.
Abel
Vous perdez aussi des choses comme ñ. Pas une solution si vous me demandez.
Ignacio Soler Garcia le
5

J'ai dû faire quelque chose de similaire mais avec une méthode StartsWith. Voici une solution simple dérivée de @Serge - appTranslator.

Voici une méthode d'extension:

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

Et pour l'un des freaks des doublures;)

    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 incensible et casse départ insensibles avec peut être appelé comme ça

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

Un moyen plus simple de supprimer les accents:

    Dim source As String = "áéíóúç"
    Dim result As String

    Dim bytes As Byte() = Encoding.GetEncoding("Cyrillic").GetBytes(source)
    result = Encoding.ASCII.GetString(bytes)
Newton Carlos Dantas
la source
-3

essayez cette surcharge sur la méthode String.Compare.

String.Compare, méthode (String, String, Boolean, CultureInfo)

Il produit une valeur int basée sur les opérations de comparaison, y compris cultureinfo. l'exemple de la page compare "Change" en en-US et en-CZ. CH en en-CZ est une seule "lettre".

exemple du lien

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
*/

par conséquent, pour les langues accentuées, vous devrez obtenir la culture, puis tester les chaînes en fonction de cela.

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


la source
C'est une meilleure approche que de comparer directement les chaînes, mais elle considère toujours la lettre de base et sa version accentuée comme différentes . Par conséquent, il ne répond pas à la question initiale, qui voulait que les accents soient ignorés.
CB