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

StackOverflow https://stackoverflow.com/questions/2175066

Вопрос

У меня есть простой TStringList.Я создаю TStringList.Сортирую по нему.

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

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

Я углубился в суть TStringList.Сортировка и это сортировка с использованием AnsiCompareStr (с учетом регистра) или AnsiCompareText (без учета регистра).Я попробовал это обоими способами, установив для значения, чувствительного к регистру, в моем 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;
Это было полезно?

Решение

Определить «правильно».
I18N. Сортировка полностью зависит от вашего языка.
Так что я полностью согласен с Парин что это не ошибка: по умолчанию Сортировать Поведение работает как предназначено, чтобы позволить I18N работать должным образом.

Нравиться Джерри упоминание, TSTringList.sort. использует Ансикомпарестр а также Ansicomparetext. (Я объясню в нескольких строках, как это делает).

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

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

  • Деликатный случай
  • Не использует ни одной локали
  • Просто сравните порядковый ценность персонажей строк

CustomSort. определяется как это:

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

По умолчанию Сортировать Метод имеет очень простую реализацию, передавая по умолчанию Сравнивать Функция называется StringListComprestrings.:

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

Итак, если вы определите свой собственный TSTringListsortCompare совместимый Сравнивать Метод, то вы можете определить свою собственную сортировку.
TSTRINGLISTORTCompare определяется как глобальная функция, принимающая TStringList, и два индекса, относящиеся к элементам, которые вы хотите сравнить:

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

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

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;

Которые затем используют функцию API лежаных Windows API Сравнение С локалом пользователя по умолчанию 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 не закрывается, совсем наоборот: часто это действительно гибкая архитектура.
Часто просто немного копаться, чтобы увидеть, где вы можете подключить к этой гибкости.

- jereen.

Другие советы

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

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

AnsiComparestr (сравнительный с locale_user_default) имеет ошибку, потому что он получает символы с пунктированием, как равный:

E1 É1 E2 É2

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

E1 E2 é1 é2

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


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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top