文字列比較でアクセント付き文字を無視する


141

C#で2つの文字列を比較し、アクセント付きの文字をアクセントなしの文字と同じように扱う必要があります。例えば:

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

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

これら2つの文字列は同じである必要がありますが(私のアプリケーションに関する限り)、これらのステートメントは両方ともfalseと評価されます。これを行う方法はC#にありますか?

回答:


251

編集2012-01-20:ああ、男の子!ソリューションは非常に単純で、フレームワークにほぼ永久に存在しています。knightpfhorが指摘したように

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

文字列から発音区別符号を取り除く関数は次のとおりです。

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

MichKapのブログRIP ...詳細。

原則は、「é」を2つの連続した文字「e」に変換することです。次に、charを反復処理し、発音区別符号をスキップします。

「héllo」は「he <acute> llo」になり、「hello」になります。

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

注:これは、同じ関数のよりコンパクトな.NET4 +フレンドリーバージョンです。

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

1
ないので、.netコアでそれを行うにはどうすればよいstring.Normalizeですか?
Andre Soares 2017年

このおかげで、私は複数回賛成できるといいのですが!ただし、アクセント付きのすべての文字を処理するわけではありません。たとえば、ð、ħ、およびøは、それぞれo、h、およびoに変換されません。これらも同様に処理する方法はありますか?
Avrohom Yisroel 2017

@AvrohomYisroel「ð」は「ラテン小文字Eth」であり、「o-with-accent」または「d-with-accent」ではなく、個別の文字です。その他は、「ラテン小文字Hストロークあり」と「ラテン小文字Oストロークあり」であり、これらも別の文字と見なすことができます
Hans Ke st ing

135

文字列を変換する必要がなく、等しいかどうかを確認するだけの場合は、

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

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

または、比較でも大文字と小文字を区別しない場合

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

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

他の誰かがこのIgnoreNonSpaceオプションに興味がある場合は、このオプションについての説明を読むことをお勧めします。 pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; 大丈夫です:)
ジムWはモニカを

msdn: "Unicode規格は、結合文字を基本文字と結合して新しい文字を生成する文字として定義します。非間隔結合文字は、レンダリング時にそれ自体で間隔位置を占有しません。"
Avlin 2014

これらの2つの文字列に対してこのメ​​ソッドは失敗しました:tarafli /TARAFLİただし、SQLサーバーは等しいと想定されています
MonsterMMORPG

2
これは、通常、SQL Serverは大文字と小文字を区別しないように構成されていますが、デフォルトでは、.Netでの比較では大文字と小文字が区別されるためです。この大文字と小文字を区別しない方法を示すために、回答を更新しました。
knightpfhor 2015年

IEqualityComparerを作成しようとしています。GetHashCodeを提供する必要があります...それを取得するにはどうすればよいですか(等しい場合は同じである必要があります)
Yepeekai

5

次のメソッドCompareIgnoreAccents(...)は、サンプルデータに対して機能します。これが私の背景情報を入手した記事です: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)));
}

拡張方法の方が良いと思います:

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

それから使用はこれです:

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

1
これはアクセント文字を '?'にします
onmyway133

4
これは破壊的な比較です。たとえば、āとēは等しいものとして扱われます。0xFFを超える文字を失うと、文字列が等しい無視アクセントであることは保証されません。
アベル

ñのようなものも失う。あなたが私に尋ねた場合、解決策ではありません。
Ignacio Soler Garcia

5

私は似たようなことをしなければなりませんでしたが、StartsWithメソッドを使用しました。@Serge-appTranslatorから派生した簡単なソリューションを次に示します。

拡張メソッドは次のとおりです。

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

そして、1つのライナーフリークのために;)

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

アクセント記号と大文字と小文字を含むstartsWithは、このように呼び出すことができます

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

0

アクセントを削除するより簡単な方法:

    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メソッドでこのオーバーロードを試してください。

String.Compareメソッド(String、String、Boolean、CultureInfo)

これは、cultureinfoを含む比較操作に基づいてint値を生成します。このページの例では、en-USとen-CZの「変更」を比較しています。en-CZのCHは単一の「文字」です。

リンクからの例

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

そのため、アクセント付き言語の場合は、文化を取得して、それに基づいて文字列をテストする必要があります。

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


これは文字列を直接比較するよりも優れた方法ですが、ベース文字とそのアクセント付きバージョンは異なると見なされます。したがって、アクセントを無視したいという元の質問には答えません。
CB
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.