web-dev-qa-db-ja.com

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

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

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

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

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

127
Jon Tackabury

編集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」という鋭い文字に変換することです。次に、文字を反復処理し、発音区別符号をスキップします。

「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);
}
236
Serge Wautier

文字列を変換する必要がなく、単に等価性を確認したい場合は、使用できます

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
}
125
knightpfhor

次のメソッド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) {
   ...
5
Ryan Cook

同様のことをする必要がありましたが、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)
2
Guish

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

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

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