如何在字符串列表中对数字进行排序?

时间:2016-09-30 03:38:19

标签: sorting delphi delphi-10.1-berlin

我有一个包含一些数字的字符串列表。我使用我写的冒泡排序对它们进行了排序。输出是:

18
20
3
44
53

我无法理解为什么以上输出而不是我的预期:

3
18
20
44
53

我错过了什么?

5 个答案:

答案 0 :(得分:9)

首先,您不需要编写自己的排序代码来对字符串列表中的字符串进行排序。字符串列表类附带一个sort函数。您可以使用它而不是编写自己的排序代码。

现在转到问题的关键点,在程序的输出中,这些值被正确排序,当被视为文本时。它们按字典或字典顺序正确排序。例如

'20' < '3'

因为文字是逐字符比较的,'2' < '3'

但您希望将值排序为数字。在这种情况下,您应首先将它们转换为数字,然后对数字而不是文本进行排序。这样做:

  • 创建一个数组TArray<Integer>,其大小可以保存字符串列表的每个值。
  • 使用StrToInt填充数组,将列表中的每个字符串转换为整数。
  • 使用Generics.Collections单元中的TArray.Sort<T>对数组进行排序。

或许更好的方法是不要将值存储为文本。将数据保存为数组或整数列表,然后对其进行排序。毕竟,如果您的数据是整数值,那么将它们存储起来是有意义的。

如果您将数据保存在数组中,则可以按上述TArray.Sort<T>进行排序。或者,如果您使用TList<T>,请使用其Sort方法。

答案 1 :(得分:5)

正如大卫在his answer中解释的那样,你将字符串排序为文本字符,而不是整数。

您可以使用TStringList.CustomSort()方法,在比较字符串时将字符串转换为整数:

function MySortProc(List: TStringList; Index1, Index2: Integer): Integer;
var
  Value1, Value2: Integer;
begin
  Value1 := StrToInt(List[Index1]);
  Value2 := StrToInt(List[Index2]);
  if Value1 < Value2 then
    Result := -1
  else if Value2 < Value1 then
    Result := 1
  else
    Result := 0;
end;

SL.Add('18');
SL.Add('20');
SL.Add('3');
SL.Add('44');
SL.Add('53');
SL.CustomSort(MySortProc);

或者,将实际的整数值存储在Objects属性中,这样您就不必在排序时连续转换字符串:

function MySortProc(List: TStringList; Index1, Index2: Integer): Integer;
var
  Value1, Value2: Integer;
begin
  Value1 := Integer(List.Objects[Index1]);
  Value2 := Integer(List.Objects[Index2]);
  if Value1 < Value2 then
    Result := -1
  else if Value2 < Value1 then
    Result := 1
  else
    Result := 0;
end;

SL.Add('18', TObject(18));
SL.Add('20', TObject(20));
SL.Add('3', TObject(3));
SL.Add('44', TObject(44));
SL.Add('53', TObject(53));
SL.CustomSort(MySortProc);

或者,您可以使用StrCmpLogicalW()让Windows为您比较字符串:

function StrCmpLogicalW(const psz1, psz2: PWideChar): Integer; stdcall; external 'Shlwapi.dll';

function MySortProc(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := StrCmpLogicalW(PChar(List[Index1]), PChar(List[Index2]));
end;

SL.Add('18');
SL.Add('20');
SL.Add('3');
SL.Add('44');
SL.Add('53');
SL.CustomSort(MySortProc);

答案 2 :(得分:0)

这是一种与Remy非常相似的方法,但它不使用CustomSort,而是使用从TStringList派生的新类来覆盖CompareStrings。该实现使用来自 System.Math CompareValue和字符串帮助函数ToInteger

优点是新类可以将Sorted属性设置为true(触发排序顺便说一句)。这使Find函数起作用,而IndexOf则使用type TIntegerStringList = class(TStringList) protected function CompareStrings(const S1: string; const S2: string): Integer; override; end; function TIntegerStringList.CompareStrings(const S1, S2: string): Integer; begin result := CompareValue(S1.ToInteger, S2.ToInteger); end; 函数。新添加的(整数)字符串也按排序顺序插入。

geom_hex

如果这不是一项要求,我也会出于性能原因选择David的解决方案。

答案 3 :(得分:-1)

您正在使用TStringList(字符串列表),因此排序适用于字符串,而不是数字。 一个简单的解决方案是使用TStringList出现的排序方法。 您必须在代码中引入一个小变体。

如果您使用TStringList,则会将Numbers转换为字符串,反之亦然。使用相同的工作,您可以在TStringList中插入数字,其格式使排序方法正常工作。

  1. 创建TStringListSorted属性为False -default - )
  2. 当您在TStringList上插入数字(并将其转换为字符串)时,请在左侧部分使用“0”。
  3.   

    00000018

         

    00000020

         

    00000003

         

    00000044

         

    00000053

    1. Sorted属性更改为True,让TStringList完成工作。
    2. 现在数字已经排序了。
    3.   

      00000003

           

      00000018

           

      00000020

           

      00000044

           

      00000053

      1. 当你获得一个数字时,用你之前的标准StrToInt转换字符串。

答案 4 :(得分:-1)

最快的方法由David描述 - 将字符串转换为整数数组,对该数组进行排序,然后如果必须(或更好地保留整数列表)则重新创建字符串列表。

我会添加最懒惰的方法。对于小型列表(大约100行)来说可能已经足够了,但它会比上面提到的正确方法慢,而且你拥有的项目越多,它就越慢。

懒惰的方法是安装Jedi代码库并使用增强的StringList。

你可以这样做:

var sl: iJclStringList;

begin
  sl := JclStringList();

  sl.Split( '18, 20, 3, 44, 53', ',' ).Trim().SortAsInteger();

  ShowMessage( sl.Join(', ') );
  ShowMessage( sl.Join(^M^J) );

  sl := nil;
end;

同样,这种方法正在进行大量冗余的额外工作,因此对于任何或多或少的重要数据量,使用整数的正确方法是首选。