字符串比较中忽略重音字母


141

我需要在C#中比较2个字符串,并将重音字母与非重音字母相同。例如:

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

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

这两个字符串必须相同(就我的应用程序而言),但是这两个语句的评估结果均为false。C#中有办法做到这一点吗?

Answers:


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',即尖峰。然后,它遍历字符并跳过变音符号。

“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
安德烈·苏亚雷斯

谢谢您,我希望能投票一次以上!但是,它不能处理所有重音字母,例如ð、,和ø不会分别转换为o,h和o。有没有办法处理这些呢?
Avrohom Yisroel

@AvrohomYisroel的“ð”是“拉丁小写字母Eth”,它是一个单独的字母,而不是“带有重音的o”或“带有重音的d”。其他的是“带笔划的拉丁文小写字母H”和“带笔划的拉丁文小写字母O”
汉斯·

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;没关系:)
Jim W说要恢复Monica 2014年

在msdn上:“ Unicode标准将组合字符定义为与基本字符组合以产生新字符的字符。非间距组合字符在呈现时自身不会占据间距位置。”
Avlin

好的,此方法对于以下2个字符串均失败:tarafli /TARAFLİ但是SQL Server说的应该与它相等
MonsterMMORPG 2015年

2
这是因为通常将SQL Server配置为不区分大小写,但默认情况下,.Net中的比较是区分大小写的。我已经更新了答案,以显示如何使此大小写不敏感。
knightpfhor

我正在尝试创建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;            
    }

对于一个班轮怪胎;)

    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方法(字符串,字符串,布尔值,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/zh-CN/library/hyxc48dt.aspx


与直接比较字符串相比,这是一种更好的方法,但是仍然认为基本字母及其重音版本不同。因此,它无法回答原始问题,该问题希望重音被忽略。
CB
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.