Как я могу заставить TStringList сортировать по-разному в Delphi

У меня есть простой TStringList. Я делаю на нем TStringList.Sort.

Затем я замечаю, что подчеркивание «_» сортируется перед заглавной буквой «А». Это отличалось от стороннего пакета, который сортировал тот же текст и сортировал _ после A.

Согласно набору символов ANSI, A-Z — это символы 65–90, а _ — 95. Таким образом, похоже, что сторонний пакет использует этот порядок, а TStringList.Sort — нет.

Я углубился в внутренности TStringList.Sort, и он сортирует с помощью AnsiCompareStr (с учетом регистра) или AnsiCompareText (без учета регистра). Я пробовал оба способа, устанавливая значение CaseSensitive моего StringList в true, а затем в false. Но в обоих случаях «_» сортируется первым.

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

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

Для справки: я использую Delphi 2009 и использую строки Unicode в своей программе.


Таким образом, окончательный ответ здесь состоит в том, чтобы переопределить сравнение Ansi с тем, что вы хотите (например, сравнение не-ANSI) следующим образом:

type
  TMyStringList = class(TStringList)
  protected
    function CompareStrings(const S1, S2: string): Integer; override;
  end;

function TMyStringList.CompareStrings(const S1, S2: string): Integer;
begin
  if CaseSensitive then
    Result := CompareStr(S1, S2)
  else
    Result := CompareText(S1, S2);
end;

person lkessler    schedule 01.02.2010    source источник
comment
Windows также сортирует _ перед A, поэтому TStringlist, по крайней мере, соответствует ОС.   -  person Lieven Keersmaekers    schedule 01.02.2010
comment
получение результатов, которых вы не ожидаете, не означает, что это ошибка. Это не ошибка, он разработан таким образом, чтобы правильно поддерживать выбор пользователем (или ОС от имени пользователя) порядка сортировки.   -  person PA.    schedule 01.02.2010
comment
Вы пишете этот вопрос, предполагая, что существует правильный способ сортировки неалфавитных символов. Где в вашем словаре появляются слова с подчеркиванием?   -  person Rob Kennedy    schedule 01.02.2010
comment
@Rob: мне не нужен правильный путь. Мне действительно нужен только последовательный способ, чтобы моя программа использовала один и тот же порядок как для TStringList, так и для моего стороннего пакета.   -  person lkessler    schedule 01.02.2010
comment
Вы упомянули в нескольких местах в своем вопросе, что вам нужен правильный или правильный порядок сортировки. Я отредактирую ваш вопрос, чтобы изменить это, поскольку это не совсем то, что вам нужно.   -  person Rob Kennedy    schedule 01.02.2010


Ответы (3)


Определите «правильно».
i18n сортировка полностью зависит от вашего региона.
Итак, Я полностью согласен с PA в том, что это не ошибка: поведение Sort по умолчанию работает так, как задумано. чтобы i18n работал правильно.

Как упоминает Герри, TStringList.Sort использует AnsiCompareStr и >AnsiCompareText (в нескольких строках я объясню, как это делается).

Но: TStringList является гибким, он содержит Sort, CustomSort и CompareStrings, которые являются виртуальными (так что вы можете переопределить их в классе-потомке)
Кроме того, когда вы вызываете CustomSort, вы можете подключить свою собственную функцию Compare.

В конце этого ответа есть функция Сравнить, которая делает то, что вы хотите:

  • Деликатный случай
  • Без использования какой-либо локали
  • Просто сравните порядковый номер символов строк

Пользовательская сортировка определяется следующим образом:

procedure TStringList.CustomSort(Compare: TStringListSortCompare);
begin
  if not Sorted and (FCount > 1) then
  begin
    Changing;
    QuickSort(0, FCount - 1, Compare);
    Changed;
  end;
end;

По умолчанию метод Sort имеет очень простую реализацию, передавая функцию Compare по умолчанию, называемую StringListCompareStrings:

procedure TStringList.Sort;
begin
  CustomSort(StringListCompareStrings);
end;

Таким образом, если вы определяете свой собственный метод Compare, совместимый с TStringListSortCompare, вы можете определить свою собственную сортировку.
TStringListSortCompare определяется как глобальная функция, принимающая TStringList и два индекса. ссылаясь на элементы, которые вы хотите сравнить:

type
  TStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer;

Вы можете использовать StringListCompareStrings в качестве руководства для реализации собственного:

function StringListCompareStrings(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := List.CompareStrings(List.FList^[Index1].FString,
                                List.FList^[Index2].FString);
end;

Итак, по умолчанию TStringList.Sort ссылается на TList.CompareStrings:

function TStringList.CompareStrings(const S1, S2: string): Integer;
begin
  if CaseSensitive then
    Result := AnsiCompareStr(S1, S2)
  else
    Result := AnsiCompareText(S1, S2);
end;

Которые затем используют базовую функцию Windows API CompareString с языковым стандартом пользователя по умолчанию LOCALE_USER_DEFAULT:

function AnsiCompareStr(const S1, S2: string): Integer;
begin
  Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(S1), Length(S1),
    PChar(S2), Length(S2)) - 2;
end;

function AnsiCompareText(const S1, S2: string): Integer;
begin
  Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PChar(S1),
    Length(S1), PChar(S2), Length(S2)) - 2;
end;

Наконец, нужная вам функция Сравнить. Опять ограничения:

  • Деликатный случай
  • Без использования какой-либо локали
  • Просто сравните порядковый номер символов строк

Это код:

function StringListCompareStringsByOrdinalCharacterValue(List: TStringList; Index1, Index2: Integer): Integer;
var
  First: string;
  Second: string;
begin
  First := List[Index1];
  Second := List[Index2];
  if List.CaseSensitive then
    Result := CompareStr(First, Second)
  else
    Result := CompareText(First, Second);
end;

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

--jeroen

person Community    schedule 01.02.2010
comment
Очень хорошо! Я знал об этом, но раньше не видел всего этого описанного в одном месте. - person Warren P; 01.02.2010
comment
Сортировка TStringList строго неверна, потому что она не соответствует порядку, заданному встроенными операторами ("‹" и т. д.), и это отклонение от порядка по умолчанию даже не задокументировано. Тот факт, что это, вероятно, было задумано как дешевая замена i18n, не делает его лучше. Проголосуйте за предоставление обходного пути, который восстанавливает правильную сортировку... - person DarthGizka; 10.04.2019
comment
Гибкий также в порядке возрастания или убывания: функция CompareSalary3(List: TStringList; Index1, Index2: Integer): integer; begin //по возрастанию: результат:= CompareString(list[index1], list[index2]); // по убыванию просто отрицаем результат result:= - CompareString(list[index1], list[index2]); конец; - person Max Kleiner; 28.01.2021

AnsiCompareStr / AnsiCompareText учитывают не только количество символов. Они учитывают языковой стандарт пользователя, поэтому «e» будет сортироваться вместе с «é», «ê» и т. д.

Чтобы отсортировать его в порядке ASCII, используйте пользовательскую функцию сравнения как описано здесь

person Gerry Coll    schedule 01.02.2010

AnsiCompareStr (CompareString с LOCALE_USER_DEFAULT) имеет ошибку, потому что он получает символы с пунктуацией как равные:

e1 é1 e2 é2

Правильный порядок (например, для чешского):

e1 e2 é1 é2

Кто-нибудь знает, как избежать этой ошибки при заказе?


11.2.2010: Должен извиниться, описанное поведение полностью соответствует лингвистическим правилам. Хотя я думаю, что это глупо и «плохо», это не ошибка в функции API.

Проводник в Windows XP использует так называемый интуитивно понятный порядок имен файлов, который дает лучшие результаты, но его нельзя использовать программно.

person Roman Stedronsky    schedule 10.02.2010