Как я могу заставить TStringList сортировать по-другому в Delphi
-
24-09-2019 - |
Вопрос
У меня есть простой 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, которое дает лучшие результаты, но его нельзя использовать программически.