如何在Delphi中以不同的方式对TStringList进行排序

时间:2010-02-01 06:34:40

标签: delphi sorting ansi tstringlist

我有一个简单的TStringList。我做了一个TStringList.Sort。

然后我注意到下划线“_”在大写字母“A”之前排序。这与排序相同文本并在A之后排序_的第三方软件包形成对比。

根据ANSI字符集,A-Z是字符65 - 90,_是95.所以看起来第三方包使用该顺序而TStringList.Sort不是。

我深入研究了TStringList.Sort的内容,并使用AnsiCompareStr(Case Sensitive)或AnsiCompareText(Case Insensitive)进行排序。我尝试了两种方法,将我的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;

3 个答案:

答案 0 :(得分:36)

定义“正确”。
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;

然后使用默认用户区域设置CompareString下面的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并未完全相反:通常它是一个非常灵活的架构 通常只需要挖掘一下,看看你可以在哪里找到灵活性。

- 的Jeroen

答案 1 :(得分:5)

AnsiCompareStr / AnsiCompareText考虑了多个字符数。他们将用户区域设置考虑在内,因此“e”将与“é”,“ê”等一起排序。

要使其按照Ascii顺序排序,请使用自定义比较函数as described here

答案 2 :(得分:0)

AnsiCompareStr(CompareString与LOCALE_USER_DEFAULT)有错误,因为它会使带有标点的字符相等:

E1 E1 E2 E2

正确的顺序是(例如捷克语):

E1 E2 E1 E2

有人知道在订购时如何避免这个错误吗?


11.2.2010:我必须道歉所描述的行为完全符合语言规则。虽然我认为它很愚蠢而且“糟糕”但它并不是API函数中的错误。

Windows XP中的

资源管理器使用所谓的直观filname排序,它可以提供更好的结果,但不能以编程方式使用。