我有一个简单的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;
答案 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排序,它可以提供更好的结果,但不能以编程方式使用。