如何在Delphi中让TStringList进行排序不同 [英] How can I get TStringList to sort differently in Delphi

查看:504
本文介绍了如何在Delphi中让TStringList进行排序不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



然后我注意到下划线_在大写字母A之前排序。这与第三方打包的方法不同,后者在A之后排序相同的文本并排序_。



根据ANSI字符集,AZ为字符65 - 90, _是95.所以看起来像第三方软件包正在使用该顺序,而TStringList.Sort不是。



我钻入了TStringList.Sort的内容,正在使用AnsiCompareStr(区分大小写)或AnsiCompareText(不区分大小写)进行排序。我尝试了两种方法,将我的StringList的CaseSensitive值设置为true,然后将其设置为false。但是在这两种情况下,_首先排序。



我无法想象这是TStringList中的错误。所以我还没有看到别的东西。可能是什么?



我真正需要知道的是如何让我的TStringList进行排序,使其与其他包的顺序相同。 p>

为了参考,我使用的是Delphi 2009,我在程序中使用Unicode字符串。






所以这里的最后一个答案是覆盖Ansi与任何你想要的比较(比如非ansi比较)如下:

  type 
TMyStringList = class(TStringList)
protected
function CompareStrings(const S1,S2:string):Integer;覆盖
结束

函数TMyStringList.CompareStrings(const S1,S2:string):整数;
begin
如果CaseSensitive然后
结果:= CompareStr(S1,S2)
else
结果:= CompareText(S1,S2);
结束


解决方案

正确定义。 b i18n 排序完全取决于您的地区。

所以我完全同意 PA 这不是一个错误:默认的排序行为的工作原理是允许i18n



Gerry 提到的 TStringList .Sort 使用 AnsiCompareStr AnsiCompareText (我将在几行中解释这一点)。



但是:TStringList是灵活的,它包含 Sort CustomSort CompareStrings ,这些都是虚拟的(所以你可以重写它们后裔课程)

此外,当您拨打 CustomSort 时,您可以插入自己的比较函数。



这个答案是一个比较功能,可以执行所需的操作:




  • 区分大小写

  • 不使用任何区域设置

  • 只需比较字符的序数值字符串



CustomSort 定义如下:

 程序TStringList.CustomSort(比较:TStringListSortCompare); 
开始
如果没有排序和(FCount> 1)然后
开始
更改;
QuickSort(0,FCount - 1,比较);
更改;
结束
结束

默认情况下, Sort 方法具有非常简单的实现,默认比较函数调用 StringListCompareStrings

  procedure TStringList.Sort; 
begin
CustomSort(StringListCompareStrings);
结束

所以,如果你定义自己的 TStringListSortCompare 兼容比较方法,那么您可以定义自己的排序。

TStringListSortCompare被定义为一个全局函数,采用TStringList,并且两个索引引用要比较的项:

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

您可以使用 StringListCompareStrings 作为实现自己的指南: / p>

  function StringListCompareStrings(List:TStringList; Index1,Index2:Integer):Integer; 
begin
结果:= List.CompareStrings(List.FList ^ [Index1] .FString,
List.FList ^ [Index2] .FString);
结束

因此,默认情况下,TStringList.Sort将延迟到TList.CompareStrings:

 函数TStringList.CompareStrings(const S1,S2:string):整数; 
begin
如果CaseSensitive然后
结果:= AnsiCompareStr(S1,S2)
else
结果:= AnsiCompareText(S1,S2);
结束

然后使用下面的Windows API函数 CompareString 与默认用户区域设置 LOCALE_USER_DEFAULT

 函数AnsiCompareStr(const S1,S2:string):整数; 
begin
结果:= CompareString(LOCALE_USER_DEFAULT,0,PChar(S1),Length(S1),
PChar(S2),Length(S2)) -
结束

函数AnsiCompareText(const S1,S2:string):整数;
begin
结果:= CompareString(LOCALE_USER_DEFAULT,NORM_IGNORECASE,PChar(S1),
长度(S1),PChar(S2),长度(S2)) - 2;
结束

最后还需要比较功能。再次限制:




  • 区分大小写

  • 不使用任何区域设置

  • 只是比较字符串的字符的顺序值



这是代码:

  function StringListCompareStringsByOrdinalCharacterValue(List:TStringList; Index1,Index2:Integer):Integer; 
var
第一个:string;
Second:string;
begin
First:= List [Index1];
Second:= List [Index2];
如果List.CaseSensitive然后
结果:= CompareStr(第一,第二)
else
结果:= CompareText(第一,第二);
结束

Delphi没有关闭,恰恰相反:通常它是一个非常灵活的架构。

通常只需要挖掘一下可以勾勒出哪种灵活性。



- jeroen


I have a simple TStringList. I do a TStringList.Sort on it.

Then I notice that the underscore "_" sorts before the capital letter "A". This was in contrast to a third party package that was sorting the same text and sorted _ after A.

According to the ANSI character set, A-Z are characters 65 - 90 and _ is 95. So it looks like the 3rd party package is using that order and TStringList.Sort isn't.

I drilled down into guts of TStringList.Sort and it is sorting using AnsiCompareStr (Case Sensitive) or AnsiCompareText (Case Insensitive). I tried it both ways, setting my StringList's CaseSensitive value to true and then false. But in both cases, the "_" sorts first.

I just can't imagine that this is a bug in TStringList. So there must be something else here that I am not seeing. What might that be?

What I really need to know is how can I get my TStringList to sort so that it is in the same order as the other package.

For reference, I am using Delphi 2009 and I'm using Unicode strings in my program.


So the final answer here is to override the Ansi compares with whatever you want (e.g. non-ansi compares) as follows:

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;

解决方案

Define "correctly".
i18n sorting totally depends on your locale.
So I totally agree with PA that this is not a bug: the default Sort behaviour works as designed to allow i18n to work properly.

Like Gerry mentions, TStringList.Sort uses AnsiCompareStr and AnsiCompareText (I'll explain in a few lines how it does that).

But: TStringList is flexible, it contains Sort, CustomSort and CompareStrings, which all are virtual (so you can override them in a descendant class)
Furthermore, when you call CustomSort, you can plug in your own Compare function.

At the of this answer is a Compare function that does what you want:

  • Case Sensitive
  • Not using any locale
  • Just compare the ordinal value of the characters of the strings

CustomSort is defined as this:

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

By default, the Sort method has a very simple implementation, passing a default Compare function called StringListCompareStrings:

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

So, if you define your own TStringListSortCompare compatible Compare method, then you can define your own sorting.
TStringListSortCompare is defined as a global function taking the TStringList and two indexes referring the items you want to compare:

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

You can use the StringListCompareStrings as a guideline for implementing your own:

function StringListCompareStrings(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := List.CompareStrings(List.FList^[Index1].FString,
                                List.FList^[Index2].FString);
end;

So, by default TStringList.Sort defers to TList.CompareStrings:

function TStringList.CompareStrings(const S1, S2: string): Integer;
begin
  if CaseSensitive then
    Result := AnsiCompareStr(S1, S2)
  else
    Result := AnsiCompareText(S1, S2);
end;

Which then use the under lying Windows API function CompareString with the default user locale 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;

Finally the Compare function you need. Again the limitations:

  • Case Sensitive
  • Not using any locale
  • Just compare the ordinal value of the characters of the strings

This is the code:

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 ain't closed, quite the opposite: often it is a really flexible architecture.
It is often just a bit of digging to see where you can hook into the that flexibility.

--jeroen

这篇关于如何在Delphi中让TStringList进行排序不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆