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?

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

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
}

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) {
   ...

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)

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)

-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


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.