Dize karşılaştırmasında aksanlı harfleri yoksayma


141

C # 2 dizeleri karşılaştırmak ve aksanlı harfleri aksanlı harflerle aynı tedavi etmek gerekir. Örneğin:

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

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

Bu 2 dizenin aynı olması gerekir (başvurum söz konusu olduğunda), ancak bu ifadelerin her ikisi de yanlış olarak değerlendirilir. C # bunu yapmak için bir yolu var mı?

Yanıtlar:


251

EDIT 2012-01-20: Ah oğlum! Çözüm çok daha basitti ve neredeyse sonsuza dek çerçevede yer aldı. Knightpfhor tarafından işaret edildiği gibi :

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

İşte bir dizeden aksan işaretleri ayıran bir işlev:

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

Daha fazla bilgi MichKap'ın blogu ( RIP ... ) hakkında.

İlke şudur: 'é' akut 2 ardışık karakter 'e' olur. Daha sonra karakterleri tekrarlar ve aksanları atlar.

"héllo", "o <acute> llo" olur ve bu da "merhaba" olur.

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

Not: İşte aynı işlevin daha kompakt bir .NET4 + dostu sürümü:

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

1
Nasıl olmadığı için .net çekirdeğinde nasıl yapılır string.Normalize?
Andre Soares

Bunun için teşekkürler, keşke birden fazla oy verebilsem! Bununla birlikte, aksanlı tüm harfleri işlemez, örneğin ð, ħ ve ø sırasıyla o, h ve o'ya dönüştürülmez. Bunları da ele almanın bir yolu var mı?
Avrohom Yisroel

@AvrohomYisroel "ð", "aksanlı o" veya "aksanlı d" değil, ayrı bir harf olan bir "Latin Küçük Harf Eth" dir. Diğerleri, ayrı harfler olarak da kabul edilebilecek "Konturlu Latin Küçük Harf H" ve "Konturlu Latin Küçük Harf O"
Hans Ke sting

135

Dizeyi dönüştürmeniz gerekmiyorsa ve sadece eşitliği kontrol etmek istiyorsanız kullanabilirsiniz

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

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

veya karşılaştırmanın büyük / küçük harfe duyarsız olmasını istiyorsanız

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

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

Başka biri bu IgnoreNonSpace seçeneğini merak ederse, bu tartışmayı okumak isteyebilirsiniz. pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; sorun değil :)
Jim W, Monica

"msgstr:" Unicode Standard, yeni bir karakter oluşturmak için karakterleri birleştirmeyi temel karakterlerle birleştirilen karakterler olarak tanımlar.
Avlin

tamam bu yöntem şu 2 dizede başarısız oldu: tarafli / TARAFLİ ancak SQL sunucusu olması gerektiği gibi eşit diyor
MonsterMMORPG

2
Bunun nedeni genellikle SQL Server'ın büyük / küçük harfe duyarlı olmayacak şekilde yapılandırılmış olmasıdır. Bu vakayı nasıl duyarsız hale getireceğimizi göstermek için cevabı güncelledim.
knightpfhor

Bir IEqualityComparer oluşturmaya çalışıyorum. GetHashCode sağlaması gerekiyor ... Bunu nasıl elde edersiniz (eşitse aynı olması gerekir)
Yepeekai

5

Aşağıdaki yöntem CompareIgnoreAccents(...)örnek verilerinizde çalışır. Arka plan bilgilerimi aldığım makale: 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)));
}

Bir uzatma yöntemi daha iyi olacağını düşünüyorum:

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

O zaman kullanım şu olurdu:

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

1
bu '?'
onmyway133

4
Bu, örneğin ā ve ē'ya eşit muamele edileceği yıkıcı bir karşılaştırmadır. 0xFF üstündeki karakterleri kaybedersiniz ve dizelerin eşit yoksayma aksanları olduğuna dair bir garanti yoktur.
Abel

Ñ ​​gibi şeyleri de kaybedersiniz. Bana sorarsan bir çözüm değil.
Ignacio Soler Garcia

5

Benzer bir şey yapmak zorunda kaldım ama bir StartsWith yöntemiyle. İşte @Serge - appTranslator'dan türetilmiş basit bir çözüm.

İşte bir uzantı yöntemi:

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

Ve bir gömlek için ucubeler;)

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

Vurgu duyarsız ve vakaya duyarlı başlangıçlar

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

0

Aksanları kaldırmanın daha basit bir yolu:

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

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

-3

String.Compare yönteminde bu aşırı yüklemeyi deneyin.

String.Compare Yöntemi (String, String, Boolean, CultureInfo)

Cultureinfo dahil karşılaştırma işlemlerine dayalı bir int değeri üretir. sayfadaki örnek en-US ve en-CZ'deki "Değiştir" i karşılaştırır. En-CZ'deki CH tek bir "harf" tir.

bağlantıdan örnek

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

bu nedenle aksanlı diller için kültürü edinmeniz ve ardından dizeleri buna göre test etmeniz gerekir.

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


Bu, dizeleri doğrudan karşılaştırmaktan daha iyi bir yaklaşımdır, ancak yine de temel harfi ve aksanlı sürümünü farklı olarak düşünmektedir . Bu nedenle aksanların göz ardı edilmesini isteyen orijinal soruya cevap vermiyor.
CB
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.