Игнорирование акцентированных букв при сравнении строк

Мне нужно сравнить 2 строки в C # и рассматривать буквы с акцентом так же, как буквы без акцента. Например:

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

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

Эти 2 строки должны быть одинаковыми (что касается моего приложения), но оба эти утверждения оцениваются как ложные. Есть ли способ в C # сделать это?

11.12.2008 15:57:05
6 ОТВЕТОВ
РЕШЕНИЕ

РЕДАКТИРОВАТЬ 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», острых. Затем он перебирает символы и пропускает диакритические знаки.

«Привет» становится «он <острый> Ило», который в свою очередь становится «Привет».

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);
}
246
23.05.2017 12:17:40
Как это сделать в ядре .net, так как его нет string.Normalize?
Andre Soares 31.03.2017 11:28:46
Спасибо за это, хотелось бы, чтобы я проголосовал не раз! Однако он не обрабатывает все акцентированные буквы, например, ð, ħ и ø не преобразуются в o, h и o соответственно. Есть ли способ справиться с этим, а?
Avrohom Yisroel 2.08.2017 14:07:28
@AvrohomYisroel «ð» - это «латинская строчная буква Eth», которая представляет собой отдельную букву, а не «o-with-accent» или «d-with-accent».
Hans Kesting 1.03.2019 11:42:55

попробуйте эту перегрузку в методе String.Compare.

Метод String.Compare (String, String, Boolean, CultureInfo)

Он генерирует значение типа int на основе операций сравнения, включая cultureinfo. Пример на странице сравнивает «Изменение» в en-US и en-CZ. CH в en-CZ - это одна буква.

пример по ссылке

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

-3
11.12.2008 16:15:40
Это лучший подход, чем прямое сравнение строк, но он по-прежнему рассматривает основную букву и ее акцентированную версию по- разному . Поэтому он не отвечает на оригинальный вопрос, который хотел, чтобы акценты были проигнорированы.
C.B. 15.05.2013 14:43:49

Следующий метод 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
11.12.2008 17:06:21
это делает акцентированное письмо к '?'
onmyway133 7.11.2012 04:51:20
Это разрушительное сравнение, где, например, А и Е будут рассматриваться как равные. Вы теряете любые символы выше 0xFF, и нет никакой гарантии, что строки равны без учета акцентов.
Abel 7.05.2013 15:18:02
Вы также теряете такие вещи, как ñ. Не решение, если вы спросите меня.
Ignacio Soler Garcia 2.02.2016 08:45:36

Если вам не нужно преобразовывать строку и вы просто хотите проверить равенство, вы можете использовать

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
}
132
13.01.2015 20:24:42
Если кому-то еще интересно узнать об этой опции IgnoreNonSpace, вы можете прочитать это обсуждение. pcreview.co.uk/forums/accent-insensitive-t3924592.html TLDR; все в порядке :)
Jim W says reinstate Monica 6.03.2014 04:25:02
на msdn: «Стандарт Unicode определяет комбинирующие символы как символы, которые объединяются с базовыми символами для создания нового символа. Непроходные комбинирующие символы сами по себе не занимают промежуточного положения при визуализации».
Avlin 24.04.2014 09:15:31
хорошо, этот метод не удался для этих 2 строк: tarafli / TARAFLİ, однако SQL-сервер говорит, что он равен
MonsterMMORPG 12.01.2015 15:38:55
Это связано с тем, что обычно SQL Server не учитывает регистр, но по умолчанию сравнения в .Net чувствительны к регистру. Я обновил ответ, чтобы показать, как сделать этот регистр нечувствительным.
knightpfhor 13.01.2015 20:25:33
Я пытаюсь создать IEqualityComparer. Он должен предоставить GetHashCode ... Как вы это получите (он должен быть таким же, если он равен)
Yepeekai 4.09.2019 21:08:14

Мне пришлось сделать что-то подобное, но с методом 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;
    }

Стартовые и ударные запуски с акцентом можно назвать так

value.ToString().StartsWith(str, CultureInfo.InvariantCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase)
5
19.12.2013 16:15:38

Более простой способ убрать акценты:

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

    Dim bytes As Byte() = Encoding.GetEncoding("Cyrillic").GetBytes(source)
    result = Encoding.ASCII.GetString(bytes)
0
1.09.2014 13:05:46