Delphi 字符串排序

Delphi String Sort

我有一个非常大的文件,如下所示

45313904626416486480179546360796323469287116537171
465573254230695450538671922463236910370073247307526
5906233480284069039032926795367974774430427486375

这种num如何排序?

结果应该是这样的(真实文件是 100000 行):

5906233480284069039032926795367974774430427486375
45313904626416486480179546360796323469287116537171
465573254230695450538671922463236910370073247307526

我尝试使用

  MyFlexibleArray := TList<UInt64>.Create;
  AssignFile(F, OpenTextFileDialog1.FileName);
  Reset(F);
  repeat
    Readln(F, str);
    MyFlexibleArray.Add(UInt64(str));
  until EOF(F);
  CloseFile(F);
  MyFlexibleArray.Sort;

对于 TStringList,结果不是以自然方式排序!

任何帮助将不胜感激。

full file

首先,您的文本文件已损坏。包含NUL字节,无法正常解析

但是,如果我们忽略这个问题,像这样对文件进行排序几乎是微不足道的。

假设没有前导零,以下算法将给出正确的结果:

var Data := TFile.ReadAllLines('K:\numbers.txt', TEncoding.ASCII);

TArray.Sort<string>(
  Data,
  TComparer<string>.Construct(
    function(const L, R: string): Integer
    begin
      Result := CompareValue(L.Length, R.Length);
      if Result <> 0 then
        Exit;
      for var i := 1 to L.Length do
      begin
        Result := CompareValue(Ord(L[i]), Ord(R[i]));
        if Result <> 0 then
          Exit;
      end;
    end
  )
);

TFile.WriteAllLines('K:\sorted.txt', Data, TEncoding.ASCII);

我们根据这些规则构造我们自己的字符串比较器:

  • 如果LR的位数多(少),那么显然它更大(更小)。
  • 如果LR的位数相同,则从MSD到LSD逐一比较。

只需将 IOUtilsGenerics.DefaultsGenerics.CollectionsMath 添加到您的 uses 子句中。

您可以将大数字保留在 TStringList 中的字符串中,并使用自定义比较器对其进行排序,该比较器首先按字符串长度排序,然后(如果长度相等)按字符串值排序。像这样:

function NumberStringComparer(List: TStringList; Index1, Index2: Integer): Integer;
var
  Value1, Value2: string;
  Len1, Len2: Integer;
begin
  Value1 := List[Index1];
  Value2 := List[Index2];
  Len1 := Length(Value1);
  Len2 := Length(Value2);

  Result := Len1 - Len2;
  if Result = 0 then
  begin
    if Value1 = Value2 then
      Result := 0
    else if Value1 > Value2 then
      Result := 1
    else
      Result := -1;
  end;
end;

用法示例:

MyStringList.CustomSort(NumberStringComparer);

如果您将数据排序为字符串,则不会考虑字符串的长度。

如果您使用 Generic(因此您的 uses 子句中需要 System.Generics.Collections),您可以指定如何将参数中的对象与构造函数进行比较。这意味着您的字符串列表将声明为:

  FMyStrings: TList<String>;

你的比较器会比较两个字符串,如果你假设字符串只能包含十进制数字那么你的比较器会是这样的:

  TMyStringSorter = class(TComparer<String>)
  public
    function Compare(const Left, Right: String): Integer; override;
  end;

  function TMyStringSorter.Compare(const Left, Right: String): Integer;
  begin
    if(Length(Left)<Length(Right) then Result:=-1
    else if(Length(Right)<Length(Left) then Result:=1
    else Result:=CompareStr(Left, Right);
  end;

然后把Interface传给比较器给TList构造函数,就可以按照自己的排序算法排序了。