Ignorando le lettere accentate nel confronto delle stringhe


141

Devo confrontare 2 stringhe in C # e trattare le lettere accentate come le lettere non accentate. Per esempio:

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

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

Queste 2 stringhe devono essere uguali (per quanto riguarda la mia domanda), ma entrambe queste affermazioni sono false. C'è un modo in C # per farlo?

Risposte:


251

EDIT 2012-01-20: Oh ragazzo! La soluzione è stata molto più semplice ed è stata nel framework quasi per sempre. Come sottolineato da knightpfhor :

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

Ecco una funzione che elimina i segni diacritici da una stringa:

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

Maggiori dettagli sul blog di MichKap ( RIP ... ).

Il principio è che trasforma 'é' in 2 caratteri successivi 'e', ​​acuto. Quindi scorre i caratteri e salta i segni diacritici.

"héllo" diventa "lui <acuto> llo", che a sua volta diventa "ciao".

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

Nota: ecco una versione più compatta di .NET4 + con la stessa funzione:

static string RemoveDiacritics(string text)
{
  return string.Concat( 
      text.Normalize(NormalizationForm.FormD)
      .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch)!=
                                    UnicodeCategory.NonSpacingMark)
    ).Normalize(NormalizationForm.FormC);
}

1
Come farlo in core .net dal momento che non ha string.Normalize?
Andre Soares,

Grazie per questo, vorrei poter votare più di una volta! Tuttavia, non gestisce tutte le lettere accentate, ad esempio ð, ħ e ø non vengono convertite rispettivamente in o, h e o. C'è un modo per gestire anche questi?
Avrohom Yisroel,

@AvrohomYisroel il "ð" è una "lettera latina etnica", che è una lettera separata, non un "o-con-accento" o "d-con-accento". Gli altri sono "Lettere latine H con tratto" e "Lettere latine O con tratto" che possono anche essere considerate lettere separate
Hans Ke sting

135

Se non è necessario convertire la stringa e si desidera solo verificare l'uguaglianza, è possibile utilizzare

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

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

o se si desidera che il confronto non faccia distinzione tra maiuscole e minuscole

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

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

Se qualcun altro è curioso di conoscere questa opzione IgnoreNonSpace, potresti voler leggere questa discussione su di essa. pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; va bene :)
Jim W dice di ripristinare Monica

su msdn: "Lo standard Unicode definisce la combinazione di caratteri come caratteri combinati con caratteri di base per produrre un nuovo carattere. I caratteri combinati non spazianti non occupano una posizione di spaziatura da soli quando vengono resi."
Avlin,

ok questo metodo non è riuscito per queste 2 stringhe: tarafli / TARAFLİ comunque SQL server dice uguale come dovrebbe essere
MonsterMMORPG

2
Questo perché generalmente SQL Server è configurato in modo che non faccia distinzione tra maiuscole e minuscole, ma per impostazione predefinita i confronti in .Net fanno distinzione tra maiuscole e minuscole. Ho aggiornato la risposta per mostrare come rendere insensibile questo caso.
knightpfhor,

Sto cercando di creare un IEqualityComparer. Deve fornire GetHashCode ... Come si ottiene (deve essere uguale se è uguale)
Yepeekai

5

Il seguente metodo CompareIgnoreAccents(...)funziona sui dati di esempio. Ecco l'articolo in cui ho ottenuto le mie informazioni di base: 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)));
}

Penso che un metodo di estensione sarebbe meglio:

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

Quindi l'uso sarebbe questo:

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

1
questo rende la lettera accentata a '?'
onmyway133,

4
Questo è un confronto distruttivo, dove ad esempio ā e ē saranno considerati uguali. Perdi qualsiasi carattere sopra 0xFF e non c'è garanzia che le stringhe siano accenti di uguale ignoranza.
Abele,

Perdi anche cose come ñ. Non è una soluzione se me lo chiedi.
Ignacio Soler Garcia,

5

Ho dovuto fare qualcosa di simile ma con un metodo StartsWith. Ecco una semplice soluzione derivata da @Serge - appTranslator.

Ecco un metodo di estensione:

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

E per i fanatici di una fodera;)

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

Avvii incensivi di accento e incensivi di maiuscole Con questo si può chiamare

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

0

Un modo più semplice per rimuovere gli accenti:

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

    Dim bytes As Byte() = Encoding.GetEncoding("Cyrillic").GetBytes(source)
    result = Encoding.ASCII.GetString(bytes)

-3

prova questo sovraccarico sul metodo String.Compare.

Metodo String.Compare (String, String, Boolean, CultureInfo)

Produce un valore int basato sulle operazioni di confronto tra cui cultureinfo. l'esempio nella pagina confronta "Modifica" in en-US e en-CZ. CH in en-CZ è una singola "lettera".

esempio dal link

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

pertanto per le lingue accentate dovrai acquisire la cultura, quindi testare le stringhe in base a ciò.

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


Questo è un approccio migliore rispetto al confronto diretto delle stringhe, ma considera ancora diversa la lettera di base e la sua versione accentata . Pertanto non risponde alla domanda originale, che voleva che gli accenti fossero ignorati.
CB,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.