Почему string.Compare непоследовательно обрабатывает символы с диакритическими знаками?

Если я выполню следующий оператор:

string.Compare("mun", "mün", true, CultureInfo.InvariantCulture)

Результат равен «-1», что указывает на то, что «мун» имеет меньшее числовое значение, чем «мюн».

Однако, если я выполню этот оператор:

string.Compare("Muntelier, Schweiz", "München, Deutschland", true, CultureInfo.InvariantCulture)

Я получаю «1», что указывает на то, что «Мунтелье, Шевиз» должно идти последним.

Это ошибка в сравнении? Или, что более вероятно, есть ли правило, которое я должен учитывать при сортировке строк, содержащих акцентированные


Причина, по которой это проблема, заключается в том, что я сортирую список, а затем выполняю ручной двоичный фильтр, который предназначен для получения каждой строки, начинающейся с «xxx».

Раньше я использовал метод «Где» Linq, но теперь мне приходится использовать эту пользовательскую функцию, написанную другим человеком, потому что, по его словам, она работает лучше.

Но пользовательская функция, похоже, не принимает во внимание любые правила «юникода», которые есть в .NET. Поэтому, если я скажу ему фильтровать по «mün», он не найдет никаких элементов, даже если в списке есть элементы, начинающиеся с «mun».

По-видимому, это происходит из-за непоследовательного порядка символов с акцентом в зависимости от того, какие символы идут после символа с акцентом.


Хорошо, я думаю, что исправил проблему.

Перед фильтром я делаю сортировку по первым n буквам каждой строки, где n — длина строки поиска.


person Jonathan    schedule 03.09.2009    source источник
comment
В такие моменты мне хотелось бы, чтобы .NET Framework была с открытым исходным кодом, чтобы я мог просто пройтись по ней в режиме отладки и выяснить, что именно она делает.   -  person Jonathan    schedule 03.09.2009
comment
@jonathanconway: можно просмотреть исходный код библиотеки базовых классов, см. blogs.msdn.com/sburke/archive/2008/01/16/   -  person Dirk Vollmar    schedule 03.09.2009
comment
@divo Спасибо за ссылку. Никогда не думал, что это возможно!   -  person Jonathan    schedule 03.09.2009


Ответы (3)


Существует работающий алгоритм разрешения конфликтов, см. http://unicode.org/reports/tr10/

Для устранения сложностей сортировки с учетом языка используется многоуровневый алгоритм сравнения. Например, при сравнении двух слов наиболее важной характеристикой является базовый символ: например, разница между буквами А и В. Различия в ударении обычно игнорируются, если есть какие-либо различия в основных буквах. Различия в регистре (прописные и строчные) обычно игнорируются, если есть какие-либо различия в основе или акцентах. Пунктуация переменная. В некоторых ситуациях знак пунктуации рассматривается как основной символ. В других ситуациях его следует игнорировать, если есть какие-либо различия в основе, ударении или падеже. Также может быть окончательный, решающий уровень, при котором, если в строке вообще нет других различий, используется (нормализованный) порядок кодовых точек.

Итак, «Munt...» и «Münc...» различаются по алфавиту и сортируются по буквам «t» и «c».

Принимая во внимание, что «мун» и «мюн» в алфавитном порядке одинаковы («у» эквивалентно «ü» в утерянных языках), поэтому коды символов сравниваются.

person Adrian    schedule 03.09.2009

Похоже, что символ с акцентом используется только в ситуации типа «тай-брейк», другими словами, если в остальном строки равны.

Вот пример кода для демонстрации:

using System;
using System.Globalization;

class Test
{
    static void Main()
    {
        Compare("mun", "mün");
        Compare("muna", "münb");
        Compare("munb", "müna");
    }

    static void Compare(string x, string y)
    {
        int result = string.Compare(x, y, true, 
                                   CultureInfo.InvariantCulture));

        Console.WriteLine("{0}; {1}; {2}", x, y, result);
    }
}

(Я также пытался добавить пробел после «n», чтобы увидеть, было ли это сделано на границах слов - это не так.)

Полученные результаты:

mun; mün; -1
muna; münb; -1
munb; müna; 1

Я подозреваю, что это правильно из-за различных сложных правил Unicode, но я недостаточно о них знаю.

Что касается того, нужно ли вам принимать это во внимание... Я бы этого не ожидал. Что вы делаете, что бросает это?

person Jon Skeet    schedule 03.09.2009

Насколько я понимаю, это все еще несколько последовательно. При сравнении с использованием CultureInfo.InvariantCulture умляут-символ ü обрабатывается как неакцентный символ u.

Поскольку строки в вашем первом примере явно не равны, результат будет не 0, а -1 (что, по-видимому, является значением по умолчанию). Во втором примере Muntelier идет последним, потому что t следует за c в алфавите.

Я не смог найти в MSDN четкой документации, объясняющей эти правила, но я обнаружил, что

string.Compare("mun", "mün", CultureInfo.InvariantCulture,  
    CompareOptions.StringSort);

и

string.Compare("Muntelier, Schweiz", "München, Deutschland", 
    CultureInfo.InvariantCulture, CompareOptions.StringSort);

дает желаемый результат.

В любом случае, я думаю, вам лучше основывать свою сортировку на определенной культуре, такой как культура текущего пользователя (если возможно).

person Dirk Vollmar    schedule 03.09.2009
comment
CompareOptions.Ordinal также может быть вариантом. С этой опцией строки будут сравниваться на основе значений Unicode. См. msdn.microsoft.com/en-us/library/< /а>. - person Dirk Vollmar; 03.09.2009