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

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

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

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

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

Я просверлился в кишки TStringList.Sort и сортировка с помощью AnsiCompareStr (Case Sensitive) или AnsiCompareText (без учета регистра). Я попробовал это в обоих направлениях, установив для значения StringList CaseSensitive значение 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;
4b9b3361

Ответ 1

Определите "правильно".
i18n сортировка полностью зависит от вашей локали.
Поэтому я полностью согласен с PA, что это не ошибка: по умолчанию поведение Сортировка работает так, чтобы позволить i18n работать правильно.

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

Но: TStringList является гибким, он содержит Сортировка, CustomSort и CompareStrings, которые все являются виртуальными (чтобы вы могли переопределить их в класс потомков)
Кроме того, когда вы вызываете 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;

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

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

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

- Йерун

Ответ 2

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

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

Ответ 3

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

e1 e1 e2 e2

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

e1 e2 e1 e2

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


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

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