Mengabaikan huruf beraksen dalam perbandingan string

141

Saya perlu membandingkan 2 string dalam C # dan memperlakukan huruf beraksen sama dengan huruf non-beraksen. Sebagai contoh:

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

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

2 string ini harus sama (sejauh menyangkut aplikasi saya), tetapi kedua pernyataan ini dinilai salah. Apakah ada cara di C # untuk melakukan ini?

Jon Tackabury
sumber

Jawaban:

251

EDIT 2012-01-20: Ya ampun! Solusinya jauh lebih sederhana dan telah berada dalam kerangka kerja hampir selamanya. Seperti yang ditunjukkan oleh knightpfhor :

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

Berikut adalah fungsi yang menghapus diakritik dari string:

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

Lebih detail di blog MichKap ( RIP ... ).

Prinsipnya adalah apakah itu mengubah 'é' menjadi 2 karakter berturut-turut 'e', ​​akut. Ini kemudian beralih melalui karakter dan melompati diakritik.

"héllo" menjadi "he <acute> llo", yang pada gilirannya menjadi "hello".

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

Catatan: Inilah versi .NET4 + friendly dari fungsi yang sama:

static string RemoveDiacritics(string text)
{
  return string.Concat( 
      text.Normalize(NormalizationForm.FormD)
      .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch)!=
                                    UnicodeCategory.NonSpacingMark)
    ).Normalize(NormalizationForm.FormC);
}
Serge Wautier
sumber
1
Bagaimana melakukannya di .net core karena tidak ada string.Normalize?
Andre Soares
Terima kasih untuk ini, saya berharap saya bisa lebih baik sekali! Namun, itu tidak menangani semua huruf beraksen, misalnya ð, ħ dan ø tidak dikonversikan menjadi masing-masing o, h dan o. Apakah ada cara untuk menangani ini juga?
Avrohom Yisroel
@AvrohomYisroel "ð" adalah "Latin Small Letter Eth", yang merupakan huruf terpisah, bukan "o-dengan-aksen" atau "d-dengan-aksen". Yang lainnya adalah "Huruf Kecil Latin H Dengan Stroke" dan "Huruf Kecil Latin O Dengan Stroke" yang juga dapat dianggap sebagai surat terpisah
Hans Ke
135

Jika Anda tidak perlu mengonversi string dan Anda hanya ingin memeriksa kesetaraan yang dapat Anda gunakan

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

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

atau jika Anda ingin perbandingan menjadi case-sensitive juga

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

if (String.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) == 0)
{
    // both strings are equal
}
knightpfhor
sumber
Jika ada orang lain yang ingin tahu tentang opsi IgnoreNonSpace ini, Anda mungkin ingin membaca diskusi ini tentangnya. pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; tidak apa-apa :)
Jim W mengatakan mengembalikan Monica
pada msdn: "Standar Unicode mendefinisikan penggabungan karakter sebagai karakter yang digabungkan dengan karakter dasar untuk menghasilkan karakter baru. Menggabungkan karakter yang tidak bergerak tidak menempati posisi jarak sendiri ketika dirender."
Avlin
ok metode ini gagal untuk 2 string ini: tarafli / TARAFLİ namun SQL server mengatakan sama seperti seharusnya
MonsterMMORPG
2
Itu karena umumnya SQL Server dikonfigurasi untuk menjadi case-sensitive tetapi secara default perbandingan dalam. Net adalah case-sensitive. Saya telah memperbarui jawaban untuk menunjukkan bagaimana membuat case ini tidak sensitif.
knightpfhor
Saya mencoba membuat IEqualityComparer. Perlu menyediakan GetHashCode ... Bagaimana Anda mendapatkan itu (harus sama jika itu sama)
Yepeekai
5

Metode berikut ini CompareIgnoreAccents(...)berfungsi pada data contoh Anda. Berikut adalah artikel tempat saya mendapatkan informasi latar belakang saya: 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)));
}

Saya pikir metode ekstensi akan lebih baik:

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

Maka penggunaannya akan seperti ini:

if(string.Compare(s1.RemoveAccents(), s2.RemoveAccents(), true) == 0) {
   ...
Ryan Cook
sumber
1
ini membuat aksen surat ke '?'
onmyway133
4
Ini adalah perbandingan destruktif, di mana misalnya ā dan ē akan diperlakukan sama. Anda kehilangan karakter apa pun di atas 0xFF dan tidak ada jaminan bahwa string sama-sama mengabaikan aksen
Abel
Anda juga kehilangan hal-hal seperti ñ. Bukan solusi jika Anda bertanya kepada saya.
Ignacio Soler Garcia
5

Saya harus melakukan sesuatu yang serupa tetapi dengan metode StartsWith. Berikut adalah solusi sederhana yang berasal dari @Serge - appTranslator.

Berikut ini adalah metode ekstensi:

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

Dan untuk satu liner aneh;)

    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 incensitive dan case incensitive startingWith dapat disebut seperti ini

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

Cara yang lebih sederhana untuk menghilangkan aksen:

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

coba kelebihan ini pada Metode String.Compare.

Metode String. Bandingkan (String, String, Boolean, CultureInfo)

Ini menghasilkan nilai int berdasarkan operasi membandingkan termasuk cultureinfo. contoh di halaman membandingkan "Ubah" di en-US dan en-CZ. CH dalam en-CZ adalah "huruf" tunggal.

contoh dari tautan

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

karenanya untuk bahasa-bahasa beraksen Anda harus mendapatkan budaya lalu menguji string berdasarkan itu.

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


sumber
Ini adalah pendekatan yang lebih baik daripada membandingkan secara langsung string, tetapi masih menganggap huruf dasar dan versi aksennya berbeda . Karena itu tidak menjawab pertanyaan awal, yang ingin aksen diabaikan.
CB