我有一个包含一些数字的字符串列表。我使用自己编写的冒泡排序对它们进行了排序。输出结果如下:
18
20
3
44
53
我不明白为什么会输出上面的内容,而不是我期望的内容:
3
18
20
44
53
我有什么遗漏吗?
我有一个包含一些数字的字符串列表。我使用自己编写的冒泡排序对它们进行了排序。输出结果如下:
18
20
3
44
53
3
18
20
44
53
'20' < '3'
因为文本是按字符比较的,而 '2' < '3'
。
但如果您希望将值按 数字 的顺序排序,则应该先将它们转换为数字,然后对数字进行排序,而不是按文本排序。请按以下方式操作:
TArray<Integer>
。StrToInt
将列表中的每个字符串转换为整数来填充数组。Generics.Collections
单位中的 TArray.Sort<T>
排序数组。或者也许更好的方法是从一开始就不要将值存储为文本。将数据保存为整数的数组或列表,然后对其进行排序。毕竟,如果您的数据具有整数值,将它们存储为整数是有意义的。
如果您将数据保存在数组中,则可以像上面描述的那样使用 TArray.Sort<T>
进行排序。如果您使用 TList<T>
,则可以利用其 Sort 方法。
正如David在他的回答中解释的那样,您正在将字符串作为文本字符进行排序,而不是作为整数。
您可以使用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);
MySortProc
的建议:您不需要检查Value1
是否比Value2
小或大。 SortProc
只需要返回零、负整数或正整数来指示哪个值更大。请使用Result := Value1 - Value2;
替换if..else
结构。 - WosiMySortProc
返回StrCmpLogicalW
的结果。 - NGLNTStringList
的新类来覆盖CompareStrings
,而非使用CustomSort
。实现中使用了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;
uses
子句中使用System.SysUtils
才能使用String.ToInteger()
辅助程序。 - Remy Lebeau您正在使用 TStringList
(字符串列表),因此排序适用于字符串,而不是数字。
一个简单的解决方案是使用带有 TStringList
的排序方法。
您必须在代码中引入一个小变量。
如果您正在使用 TStringList
,则需要将数字转换为字符串,反之亦然。通过相同的工作,您可以使用使排序方法正确工作的格式将数字插入到 TStringList
中。
TStringList
(Sorted
属性为 False -默认值-)TStringList
(并将其转换为字符串)时,请在左侧部分使用“0”。00000018
00000020
00000003
00000044
00000053
Sorted
属性为 True
并让 TStringList
完成工作。00000003
00000018
00000020
00000044
00000053
StrToInt
将字符串转换。最快的方法是由David描述的 - 将字符串转换为整数数组,对该数组进行排序,然后如果必须重新创建字符串列表(或更好地保持整数列表)。
我想添加最懒的方法。对于小型列表(约100行左右),它可能足够快,但比前面提到的正确方法要慢,并且随着项目数量的增加,速度会变得越来越慢。
懒惰的方法是安装Jedi Code Library并使用增强的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;
再说一遍,这种方法会做很多冗余的额外工作,因此对于任何更多或更少的重要数据量,使用整数的正确方式是更可取的。