Reputation: 23
I have a file with very big num like below
45313904626416486480179546360796323469287116537171
465573254230695450538671922463236910370073247307526
5906233480284069039032926795367974774430427486375
How to sort this kind of num ?
The result should be something like (real file is 100000 lines):
5906233480284069039032926795367974774430427486375
45313904626416486480179546360796323469287116537171
465573254230695450538671922463236910370073247307526
I try something with
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;
With a TStringList, the result wasn't sort in natural way!
Any help will be greatly appreciated.
Upvotes: 1
Views: 1080
Reputation: 2293
If you sort your data as a string, the length of the string is not being taken into account.
If you use a Generic
(so you will need System.Generics.Collections
in your uses
clause) you can specify how to compare objects in a parameter to the constructor. This means that your list of strings would be declared as:
FMyStrings: TList<String>;
You comparator would compare two strings, if you assume that the strings can only ever contain decimal digits then your comparator would be something like:
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;
Then pass the Interface to the comparer to the TList Constructor and you can sort it according to your own sort algorithm.
Upvotes: 2
Reputation: 1025
You could keep the big numbers in strings in a TStringList, and sort it using a custom comparer that first sorts by string length and then (if length is equal) by string value. Like this:
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;
Usage example:
MyStringList.CustomSort(NumberStringComparer);
Upvotes: 0
Reputation: 108948
First, your text file is corrupt. It contains NUL bytes, which makes it impossible to parse it normally.
However, if we disregard this issue, sorting a file like this is almost trivial.
Assuming there are no leading zeros, the following algorithm will give the correct result:
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);
We construct our own string comparer according to these rules:
L
has more (fewer) digits than R
, then clearly it is greater (smaller).L
and R
has the same number of digits, compare the digits, one by one, from the MSD to the LSD.Just add IOUtils
, Generics.Defaults
, Generics.Collections
, and Math
to your uses
clause.
Upvotes: 4