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?

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

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
}

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

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)

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)

-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


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.