Bỏ qua các chữ cái có dấu trong so sánh chuỗi


141

Tôi cần so sánh 2 chuỗi trong C # và xử lý các chữ cái có dấu giống như các chữ cái không có dấu. Ví dụ:

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

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

Hai chuỗi này cần phải giống nhau (theo như ứng dụng của tôi có liên quan), nhưng cả hai câu lệnh này đều đánh giá là sai. Có cách nào trong C # để làm điều này không?

Câu trả lời:


251

EDIT 2012-01-20: Ôi cậu bé! Giải pháp đơn giản hơn rất nhiều và đã ở trong khuôn khổ gần như mãi mãi. Như được chỉ ra bởi hiệp sĩ :

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

Đây là một hàm loại bỏ dấu phụ từ một chuỗi:

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

Thêm chi tiết trên blog của MichKap ( RIP ... ).

Nguyên tắc là nó biến 'é' thành 2 ký tự liên tiếp 'e', ​​cấp tính. Sau đó, nó lặp đi lặp lại qua các ký tự và bỏ qua các dấu phụ.

"Héllo" trở thành "anh <cấp> llo", từ đó trở thành "xin chào".

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

Lưu ý: Đây là phiên bản .NET4 + nhỏ gọn hơn có cùng chức năng:

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

1
Làm thế nào để làm điều đó trong lõi .net vì nó không có string.Normalize?
Andre Soares

Cảm ơn vì điều này, tôi ước tôi có thể nâng cao hơn một lần! Tuy nhiên, nó không xử lý tất cả các chữ cái có dấu, ví dụ ð, và ø không được chuyển đổi thành o, h và o tương ứng. Có cách nào để xử lý những điều này là tốt?
Avrohom Yisroel

@AvrohomYisroel "ð" là "Eth nhỏ chữ Latinh", là một chữ cái riêng biệt, không phải là "o-with-accent" hay "d-with-accent". Các chữ cái khác là "Chữ nhỏ La tinh H có chữ đột quỵ" và "Chữ nhỏ chữ Latinh chữ O có nét" cũng có thể được coi là các chữ cái riêng biệt
Hans Ke st ing

135

Nếu bạn không cần phải chuyển đổi chuỗi và bạn chỉ muốn kiểm tra sự bằng nhau, bạn có thể sử dụng

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

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

hoặc nếu bạn muốn so sánh cũng không nhạy cảm

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

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

Nếu bất cứ ai khác tò mò về tùy chọn IgnoreNonSpace này, bạn có thể muốn đọc cuộc thảo luận này về nó. pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; không sao đâu :)
Jim W nói phục hồi Monica

trên msdn: "Tiêu chuẩn Unicode định nghĩa kết hợp các ký tự là các ký tự được kết hợp với các ký tự cơ sở để tạo ra một ký tự mới. Không ký tự kết hợp các ký tự không chiếm vị trí khoảng cách khi được hiển thị."
Avlin

ok phương pháp này không thành công cho 2 chuỗi này: tarafli / TARAFLftime tuy nhiên máy chủ SQL nói tương đương như được cho là
MonsterMMORPG

2
Đó là bởi vì SQL Server thường được cấu hình không phân biệt chữ hoa chữ thường nhưng theo mặc định so sánh trong .Net phân biệt chữ hoa chữ thường. Tôi đã cập nhật câu trả lời để chỉ ra cách làm cho trường hợp này không nhạy cảm.
hiệp sĩ

Tôi đang cố gắng tạo IEqualityComparer. Nó cần phải cung cấp GetHashCode ... Làm thế nào để bạn có được điều đó (nó cần giống nhau nếu nó bằng nhau)
Yepeekai

5

Phương pháp sau đây CompareIgnoreAccents(...)hoạt động trên dữ liệu ví dụ của bạn. Đây là bài viết mà tôi đã nhận được thông tin cơ bản của mình: 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)));
}

Tôi nghĩ rằng một phương pháp mở rộng sẽ tốt hơn:

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

Sau đó, việc sử dụng sẽ là:

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

1
Điều này làm cho chữ có dấu thành '?'
onmyway133

4
Đây là một so sánh mang tính hủy diệt, trong đó, ví dụ ā và sẽ được coi là bằng nhau. Bạn mất bất kỳ ký tự nào trên 0xFF và không có gì đảm bảo rằng các chuỗi là dấu bỏ qua bằng nhau.
Abel

Bạn cũng mất những thứ như ñ. Không phải là một giải pháp nếu bạn hỏi tôi.
Ignacio Soler Garcia

5

Tôi đã phải làm một cái gì đó tương tự nhưng với một phương pháp StartsWith. Đây là một giải pháp đơn giản bắt nguồn từ @Serge - appTranslator.

Đây là một phương pháp mở rộng:

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

Và đối với một quái vật lót;)

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

Accentitive incensitive và case incensitive startedWith có thể được gọi như thế này

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

0

Một cách đơn giản hơn để loại bỏ dấu:

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

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

-3

thử quá tải này trên Phương thức String.Compare.

Phương thức String.Compare (Chuỗi, Chuỗi, Boolean, CultureInfo)

Nó tạo ra một giá trị int dựa trên các hoạt động so sánh bao gồm cả văn hóa. ví dụ trong trang so sánh "Thay đổi" trong en-US và en-CZ. CH trong en-CZ là một "chữ cái" duy nhất.

ví dụ từ liên kết

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

Do đó, đối với các ngôn ngữ có dấu, bạn sẽ cần có văn hóa sau đó kiểm tra các chuỗi dựa trên đó.

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


Đây là một cách tiếp cận tốt hơn so với việc so sánh trực tiếp các chuỗi, nhưng nó vẫn coi chữ cái cơ sở và phiên bản có dấu của nó khác nhau . Vì vậy, nó không trả lời câu hỏi ban đầu, mà muốn dấu bị bỏ qua.
CB
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.