Ignorieren von Buchstaben mit Akzent im Zeichenfolgenvergleich

141

Ich muss 2 Zeichenfolgen in C # vergleichen und Buchstaben mit Akzent genauso behandeln wie Buchstaben ohne Akzent. Beispielsweise:

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

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

Diese beiden Zeichenfolgen müssen identisch sein (für meine Anwendung), aber beide Aussagen werden als falsch bewertet. Gibt es in C # eine Möglichkeit, dies zu tun?

Jon Tackabury
quelle

Antworten:

251

EDIT 2012-01-20: Oh Junge! Die Lösung war so viel einfacher und war fast für immer im Rahmen. Wie von knightpfhor hervorgehoben :

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

Hier ist eine Funktion, mit der diakritische Zeichen von einem String entfernt werden:

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

Weitere Details auf MichKaps Blog ( RIP ... ).

Das Prinzip ist, dass es 'é' in 2 aufeinanderfolgende Zeichen 'e' verwandelt, akut. Es durchläuft dann die Zeichen und überspringt die diakritischen Zeichen.

"héllo" wird zu "he <acute> llo", was wiederum zu "hallo" wird.

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

Hinweis: Hier ist eine kompaktere .NET4 + -freundliche Version derselben Funktion:

static string RemoveDiacritics(string text)
{
  return string.Concat( 
      text.Normalize(NormalizationForm.FormD)
      .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch)!=
                                    UnicodeCategory.NonSpacingMark)
    ).Normalize(NormalizationForm.FormC);
}
Serge Wautier
quelle
1
Wie mache ich das in .net Core, da es nicht hat string.Normalize?
Andre Soares
Vielen Dank dafür, ich wünschte, ich könnte mehr als einmal upvoten! Es werden jedoch nicht alle Buchstaben mit Akzent behandelt, z. B. werden ð, ħ und ø nicht in o, h bzw. o konvertiert. Gibt es eine Möglichkeit, auch damit umzugehen?
Avrohom Yisroel
@ AvrohomYisroel das "ð" ist ein "lateinischer Kleinbuchstabe Eth", der ein separater Buchstabe ist, kein "o-mit-Akzent" oder "d-mit-Akzent". Die anderen sind "Lateinischer Kleinbuchstabe H mit Strich" und "Lateinischer Kleinbuchstabe O mit Strich", die auch als separate Buchstaben betrachtet werden können
Hans Keing
135

Wenn Sie den String nicht konvertieren müssen und nur auf Gleichheit prüfen möchten, können Sie ihn verwenden

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

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

oder wenn Sie möchten, dass der Vergleich auch nicht zwischen Groß- und Kleinschreibung unterscheidet

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

if (String.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) == 0)
{
    // both strings are equal
}
Ritterpfhor
quelle
Wenn jemand anderes neugierig auf diese IgnoreNonSpace-Option ist, sollten Sie diese Diskussion darüber lesen. pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; es ist in Ordnung :)
Jim W sagt, Monica am
on msdn: "Der Unicode-Standard definiert das Kombinieren von Zeichen als Zeichen, die mit Basiszeichen kombiniert werden, um ein neues Zeichen zu erzeugen. Nicht räumliche Kombinationszeichen nehmen beim Rendern keine Abstandsposition für sich ein."
Avlin
ok diese Methode ist für diese 2 Strings fehlgeschlagen: tarafli / TARAFLİ jedoch sagt SQL Server gleich wie angenommen
MonsterMMORPG
2
Dies liegt daran, dass SQL Server im Allgemeinen so konfiguriert ist, dass die Groß- und Kleinschreibung nicht berücksichtigt wird. Bei Vergleichen in .Net wird jedoch standardmäßig zwischen Groß- und Kleinschreibung unterschieden. Ich habe die Antwort aktualisiert, um zu zeigen, wie dieser Fall unempfindlich gemacht werden kann.
Knightpfhor
Ich versuche einen IEqualityComparer zu erstellen. Es muss GetHashCode bereitstellen ... Wie bekommt man das (es muss gleich sein, wenn es gleich ist)
Yepeekai
5

Die folgende Methode CompareIgnoreAccents(...)arbeitet mit Ihren Beispieldaten. Hier ist der Artikel, in dem ich meine Hintergrundinformationen erhalten habe: 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)));
}

Ich denke, eine Erweiterungsmethode wäre besser:

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

Dann wäre die Verwendung folgende:

if(string.Compare(s1.RemoveAccents(), s2.RemoveAccents(), true) == 0) {
   ...
Ryan Cook
quelle
1
Dies macht einen akzentuierten Brief an '?'
onmyway133
4
Dies ist ein destruktiver Vergleich, bei dem beispielsweise ā und ē als gleich behandelt werden. Sie verlieren Zeichen über 0xFF und es gibt keine Garantie dafür, dass die Zeichenfolgen gleich ignorierende Akzente haben.
Abel
Sie verlieren auch Dinge wie ñ. Keine Lösung, wenn Sie mich fragen.
Ignacio Soler Garcia
5

Ich musste etwas Ähnliches tun, aber mit einer StartsWith-Methode. Hier ist eine einfache Lösung, die von @Serge - appTranslator abgeleitet ist.

Hier ist eine Erweiterungsmethode:

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

Und für einen Liner Freaks;)

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

Akzentinkensitiv und Groß- / Kleinschreibung werden gestartet. Mit kann so aufgerufen werden

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

Eine einfachere Möglichkeit, Akzente zu entfernen:

    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
quelle
-3

Versuchen Sie diese Überladung mit der String.Compare-Methode.

String.Compare-Methode (String, String, Boolean, CultureInfo)

Es erzeugt einen int-Wert basierend auf den Vergleichsoperationen einschließlich der Kulturinfo. Das Beispiel auf der Seite vergleicht "Änderung" in en-US und en-CZ. CH in en-CZ ist ein einzelner "Buchstabe".

Beispiel aus dem 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
*/

Für akzentuierte Sprachen müssen Sie daher die Kultur erhalten und dann die darauf basierenden Zeichenfolgen testen.

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


quelle
Dies ist ein besserer Ansatz als direkt die Saiten zu vergleichen, aber es hält nach wie vor die Basis Brief und seine akzentuierte Version unterschiedlich . Daher wird die ursprüngliche Frage nicht beantwortet, bei der Akzente ignoriert werden sollen.
CB