Подтвердить что ты не робот

Как сравнить строки unicode, содержащие неанглийские символы, для сортировки по алфавиту?

Я пытаюсь сортировать массив/списки/независимо от данных, основанных на строковых значениях unicode в них, которые содержат неанглийские символы, я хочу, чтобы они отсортировались правильно в алфавитном порядке.

Я написал много кода (D2010, win XP), который, как я думал, был достаточно прочным для будущей интернационализации, но это не так. Его все использует тип unicodestring (string), который до сих пор я просто помещал английские символы в строки unicode.

Кажется, мне приходится иметь дело с серьезной ошибкой unicode. Я поговорил с моим немецким другом и попробовал некоторые немецкие ß, (ß "ss" и должен появиться после S и до T в алфавите), и ö и т.д. (Обратите внимание на умлаут), и ни один из моих алгоритмов сортировки больше не работает. Результаты очень смешаны. Garbage.

С тех пор я много читаю и узнал много неприятных вещей в отношении сортировки юникода. Все выглядит мрачно, гораздо мрачнее, чем я ожидал, я серьезно испортил это. Надеюсь, что у меня что-то не хватает, и на самом деле все не так мрачно, как сейчас. Я не занимался просмотром вызовов windows api (RtlCompareUnicodeString) без успеха (ошибки защиты), я не мог заставить его работать. Проблема с вызовами API, которые я узнал, заключается в том, что они меняются на разных новых платформах Windows, а также с delphi, идущими на Cross Plat в ближайшее время, с linux позже, мое приложение - это клиентский сервер, поэтому мне нужно беспокоиться об этом, но tbh с ситуацией это (плохо), я был бы признателен за любой поступательный прогресс, т.е. выиграть api specific.

Использует функцию win api RtlCompareUnicodeString для очевидного решения? Если это так, я должен попробовать еще раз с этим, но tbh. Я был ошеломлен всеми проблемами, связанными с сортировкой юникодов, и я не совсем понимаю, что я должен делать, чтобы сравнивать эти строки таким образом в любом случае.

Я узнал о проекте open source IBM ICU С++, для него есть оболочка delphi, хотя для более старой версии ICU. Это очень комплексное решение, которое является независимым от платформы. Конечно, я не могу смотреть на создание оболочки delphi для этого (или обновления существующего), чтобы получить хорошее решение для сортировки unicode?

Я был бы очень рад услышать совет на двух уровнях: -

A) Нестандартное решение для Windows, я был бы рад тому, что на данный момент забыл о последствиях клиентского сервера! B) Более портативное решение, которое невосприимчиво к различным вариациям XP/vista/win7 функций api для юникода, поэтому я хорошо поддерживаю поддержку Mac OS XE2 и поддержку будущего Linux, не говоря уже о осложнениях на клиентском сервере.

Btw Я действительно не хочу делать решения "make-do", сканировать строки перед сравнением и заменять некоторые сложные символы и т.д., о которых я читал. Я дал немецкий экзамен выше, это просто пример, я хочу, чтобы он работал на всех (или, по крайней мере, на большинстве, на дальнем востоке, русском) языках, я не хочу делать обходные пути для определенного языка или двух. Мне также не нужны какие-либо рекомендации по алгоритмам сортировки, они в порядке, это просто бит сравнения строк, который был неправильным.

Надеюсь, что я упускаю/делаю что-то глупое, все это выглядит головной болью.

Спасибо.


EDIT, Руди, вот как я пытался вызвать RtlCompareUnicodeString. Извините за задержку, с которой у меня было ужасное время.

program Project26

{$APPTYPE CONSOLE}

uses
  SysUtils;


var
  a,b:ansistring;

  k,l:string;
  x,y:widestring;
  r:integer;

procedure RtlInitUnicodeString(
  DestinationString:pstring;
  SourceString:pwidechar) stdcall; external 'NTDLL';

function RtlCompareUnicodeString(
  String1:pstring;
  String2:pstring;
  CaseInSensitive:boolean
  ):integer stdcall; external 'NTDLL';


begin

  x:='wef';
  y:='fsd';

  RtlInitUnicodeString(@k, pwidechar(x));
  RtlInitUnicodeString(@l, pwidechar(y));

  r:=RtlCompareUnicodeString(@k,@l,false);

  writeln(r);
  readln;

end.

Я понимаю, что это скорее всего неправильно, я не привык к вызову api unctions напрямую, это мое лучшее предположение.

О вашей функции apiStringCompareEx. Это выглядело очень хорошо, но доступно только для Vista +, я использую XP. StringCompare находится на XP, но это не Unicode!

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

Может ли кто-нибудь сказать наверняка, должен ли это делать ansicomparetext или нет? Это не работает для меня, но другие сказали, что это нужно, и другие вещи, которые я прочитал, предлагают это.

Это то, что я получаю с 31 тестовой строкой при использовании AnsiCompareText, когда в German Locale (пробел ограничен - строки не содержат пробелов): -

  • arß Asß asß aßs no nö ö ön oo öö oöo öoö öp pöss SS ßaß ßbß sß Sßa Sßb ßß ssss SSSS ßßß ssßß SSßß ßz ßzß z zzz

ИЗМЕНИТЬ 2.

Я все еще хочу услышать, должен ли я ожидать, что AnsiCompareText будет работать с использованием информации о локали, как сказал lkessler, и lkessler также опубликовал об этих предметах раньше и, кажется, уже прошел это раньше.

Однако, следуя рекомендациям Rudy, я также проверял CompareStringW, который разделяет ту же документацию с CompareString, поэтому НЕ НЕ -никод, как я уже говорил ранее.

Даже если AnsiCompareText не будет работать, хотя я думаю, что он должен работать, функция win32api CompareStringW действительно должна работать. Теперь я определил свою функцию API, и я могу назвать ее, и я получаю результат, и никакой ошибки... но я получаю тот же результат каждый раз, независимо от входных строк! Он возвращает 1 каждый раз, что означает меньше. Здесь мой код

var
  k,l:string;

function CompareStringW(
  Locale:integer;
  dwCmpFlags:longword;
  lpString1:pstring;
  cchCount1:integer;
  lpString2:pstring;
  cchCount2:integer
  ):integer stdcall; external 'Kernel32.dll';

begin;

  k:='zzz';
  l:='xxx';

  writeln(length(k));
  r:=comparestringw(LOCALE_USER_DEFAULT,0,@k,3,@l,3);

  writeln(r); // result is 1=less than, 2=equal, 3=greater than
  readln;

end;

Я чувствую, что теперь я получаю кое-что после боли. Был бы рад узнать об AnsiCompareText и о том, что я делаю неправильно с вышеупомянутым вызовом CompareStringW api. Спасибо.


ИЗМЕНИТЬ 3

Во-первых, я исправил api-вызов для CompareStringW сам, я проходил в @mystring, когда мне нужно было PString (mystring). Теперь все работает правильно.

r:=comparestringw(LOCALE_USER_DEFAULT,0,pstring(k),-1,pstring(l),-1);

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

  • arß asß aßs Asß no nö ö ön oo öö oöo öoö öp pöss SS ßaß ßbß sß Sßa Sßb ßß ssss SSSS ßßß ssßß SSßß ßz ßzß z zzz

Вы также можете представить себе мое ЧРЕЗВЫЧАЙНОЕ смятение, не говоря уже о одновременной радости, когда я понял, что порядок сортировки ПРАВИЛЬНО, и ЭТО ПРАВИЛЬНО ПРАВИЛЬНО НАХОДИТСЯ В ПОЛУЧЕНИИ! Это заставило sme больно сказать это, но в первую очередь никогда не было никаких проблем - все это связано с отсутствием немецкого знания. Я полагал, что это было неправильно, так как вы можете видеть, что начало строки начинается с S, а затем они начинаются с ß, затем с снова и обратно до ß и так далее. Ну, я не могу говорить по-немецки, но я все еще мог ясно видеть, что они не были отсортированы правильно - мой немецкий друг сказал мне, что ß приходит после S и до T... Я был НЕПРАВИЛЬНО! Что происходит, так это то, что строковые функции (как AnsiCompareText, так и winapi CompareTextW) представляют собой все "ß" с "ss" и каждый "ö" с нормальным "o"... поэтому, если я беру эти результаты выше и на поиск и замените, как описано, я получаю...

  • arss asss asss Asss no no o on oo oo ooo ooo op po ss SS ssass ssbss sss Sssa Sssb ssss ssss SSSS ssssss ssssss SSssss ssz sszss z zzz

Выглядит довольно правильно для меня! И это всегда было.

Я чрезвычайно благодарен за все предоставленные советы и очень сожалею, что потратил ваше время так. Эти немецкие ß заставили меня смутить, никогда не было ничего плохого в встроенной функции дельфи или что-то еще. Это было похоже на то, что было. Я допустил ошибку, объединив их с обычными 's' в моих тестовых данных, любое другое письмо не создало бы эту иллюзию неупорядоченности! Улыбка ß заставила меня выглядеть дураком! ßs!

Rudy и lkessler мы оба особенно полезны, и то и другое, я должен принять lkessler ответ как самый правильный, извините Руди.

4b9b3361

Ответ 1

Попробуйте использовать CompareStr для чувствительности к регистру или CompareText для нечувствительности к регистру, если вы хотите, чтобы ваши сортировки были одинаковыми в любой локали.

И используйте AnsiCompareStr для чувствительности к регистру, или AnsiCompareText для нечувствительности к регистру, если вы хотите, чтобы ваши сортировки были специфичными для языкового стандарта пользователя.

Смотрите: Как я могу заставить TStringList сортировать по-разному в Delphi для получения более подробной информации об этом.

Ответ 2

Вы сказали, что у вас проблемы с вызовом вызовов Windows API. Не могли бы вы опубликовать код, чтобы люди могли понять, почему это не удалось? Это не так сложно, как может показаться, но это требует некоторой осторожности. ISTM, что RtlCompareUnicodeStrings() слишком низкий уровень.

Я нашел несколько решений:

непортабельного

Вы можете использовать функцию Windows API CompareStringEx. Это будет сравниваться с использованием специфических типов сортировки Unicode. Вы можете указать, как это сделать (см. Ссылку). Это требует больших строк, т.е. PWideChar указывает на них. Если у вас есть проблемы с вызовом, дайте крик, и я попытаюсь добавить демо-код.

Более-менее портативный

Чтобы сделать это более или менее переносимым, вы можете написать функцию, которая сравнивает две строки и использует условные определения для выбора различных API сравнения для платформы.

Ответ 3

В Юникоде числовой порядок символов, разумеется, не является порядком сортировки. AnsiCompareText, упомянутый HeartWare, учитывает специфику локали при сравнении символов, но, как вы выяснили, ничего не делает по порядку сортировки. То, что вы ищете, называется последовательностью сортировки языка, которая определяет порядок алфавитной сортировки для языка, принимающего диакритические знаки и т.д. Они как бы подразумевались на старых страницах кода Ansi, хотя они не учитывали разницу между языками, использующими один и тот же набор символов.

Я проверил документы D2010. Помимо некоторых компонентов TIB * я не нашел ссылок. Создатель С++, похоже, имеет функцию сравнения, которая учитывает учет, но это не так много используется в Delphi. Там вам, вероятно, придется использовать некоторые функции API Windows напрямую.

Docs:

В статье "Сортировка" Collate "all out" находится Майкл Каплан, кто-то, у которого есть глубокие знания обо всех вещах Unicode и всех тонкостях разных языков. Его блог был бесценен для меня при портировании с D2006 на D2009.

Ответ 4

Вы пробовали AnsiCompareText? Несмотря на то, что он называется "Ansi", я считаю, что он призывает к совместимой с Unicode процедуре сравнения...

Он также должен сделать вас безопасным от кросс-платформенных зависимостей (при условии, что Embarcadero поставляет совместимую версию в различные ОС, на которые они нацелены).

Я не знаю, насколько хорошо сравнение работает с различными странными способами Unicode для кодирования строк, но попробуйте и сообщите нам результат...