Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Sorting numbers as strings


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 6 - Last Post: Aug 30, 2017 4:56 AM Last Post By: Antonio Tortosa...
Gerald Holdsworth

Posts: 76
Registered: 5/23/12
Sorting numbers as strings  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 7, 2017 12:20 PM
I've got a TStringList containing the filenames:
1.bmp,2.bmp,3.bmp,4.bmp,...10.bmp,11.bmp,12.bmp,...etc.
I've noticed that both Windows and OSX sort these (in Explorer/Filer) in the order above, but a TStringList.Sort will sort them thus:
1.bmp,10.bmp,11.bmp,12.bmp,...2.bmp,20.bmp,...etc. as you would expect (as it is sorting by ASCII).

Is there some easy trick to sort them as Windows and Mac OS does?
Or am I going to have to write something to do it instead?

Cheers,

Gerald.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Sorting numbers as strings  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 7, 2017 2:37 PM   in response to: Gerald Holdsworth in response to: Gerald Holdsworth
Gerald Holdsworth wrote:

I've noticed that both Windows and OSX sort these (in Explorer/Filer)
in the order above

That is because they know they are displaying filenames and will
perform hueristics on them, which includes extracting the numbers from
the filenames and using the numbers during the sorting algorithm.

Also see:

The sort order for files and folders whose names contain numerals is
different in Windows Vista, Windows XP, and Windows Server 2003 than it
is in Windows 2000

https://support.microsoft.com/en-us/help/319827/the-sort-order-for-files-and-folders-whose-names-contain-numerals-is-d

but a TStringList.Sort will sort them thus

Because it doesn't know anything about the numbers encoded in the
filenames, it is just sorting the string characters as-is.

Is there some easy trick to sort them as Windows and Mac OS does?

Since you know the format of your filenames, you can use
TStringList.CustomSort() and do your own parsing, for example:

function SortFileNames(List: TStringList; Index1, Index2: Integer):
Integer;
var
  Num1, Num2: Integer;
begin
  Num1 := StrToInt(ChangeFileExt(ExtractFileName(List[Index1]), ''));
  Num2 := StrToInt(ChangeFileExt(ExtractFileName(List[Index2]), ''));
  Result := Num2 - Num1;
end;
 
MyList.CustomSort(SortFileNames);


Or, on Windows only, you can use the Win32 StrCmpLogicalW() function:

https://msdn.microsoft.com/en-us/library/bb759947.aspx

function StrCmpLogicalW(psz1, psz2: PWideChar): Integer; stdcall;
external 'Shlwapi.dll';
 
function SortFileNames(List: TStringList; Index1, Index2: Integer):
Integer;
begin
  Result := StrCmpLogicalW(PChar(List[Index1]), PChar(List[Index2]));
end;
 
MyList.CustomSort(SortFileNames);


--
Remy Lebeau (TeamB)
Gerald Holdsworth

Posts: 76
Registered: 5/23/12
Re: Sorting numbers as strings  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 8, 2017 11:35 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
{quote:title=Remy Lebeau (TeamB) wrote:}
Since you know the format of your filenames, you can use

I only know the format for the test files I'm using. Others might have different formats of filenames.

Or, on Windows only, you can use the Win32 StrCmpLogicalW() function:

https://msdn.microsoft.com/en-us/library/bb759947.aspx

That looks like it could do the trick. I'll give it a go.

Cheers,

Gerald.
Gerald Holdsworth

Posts: 76
Registered: 5/23/12
Re: Sorting numbers as strings  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 9, 2017 2:35 AM   in response to: Gerald Holdsworth in response to: Gerald Holdsworth
That looks like it could do the trick. I'll give it a go.
It does work...lovely.

However, on trying to figure out how to get the procedures into the code (I first had them as inside the procedure that used them, then as part of the unit), and failing, I looked up on how to include them and found the following code:
unit U_CustomStringListSort;
{Copyright � 2013, Gary Darby,  www.DelphiForFun.org
 This program may be used or modified for any non-commercial purpose
 so long as this original notice remains in place.
 All other rights are reserved
 }
 
 
 
interface
 
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  shellAPI, StdCtrls, ExtCtrls, ComCtrls;
 
type
  TForm1 = class(TForm)
    StaticText1: TStaticText;
    Panel1: TPanel;
    Memo1: TMemo;
    Button1: TButton;
    Memo2: TMemo;
    Memo3: TMemo;
    Label2: TLabel;
    Label3: TLabel;
    procedure StaticText1Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  public
 end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.DFM}
 
 //var    {Variables temporarily moved outside of FixKey for debugging}
 //  i,j:integer;
 
  {*************** FixKey ************}
  procedure fixkey(key:string; var fix1:string; var fix2:integer; var fix3:string);
  {{Parse string "key" into alpha, numeric, and alpa parts}
   var
     i,j:integer;
   begin
     i:=1;
     while (i<=length(key)) and (not (key[i] in ['0'..'9'])) do inc(i);
     if i<=length(key) then
     begin {digits found}
       fix1:=uppercase(copy(key,1,i-1));
       j:=i;
       while (j<=length(key)) and (key[j] in ['0'..'9']) do inc(j);
       fix2:=strtoint(copy(key,i, j-i));
       fix3:=uppercase(copy(key,j,length(key)-j+1));
     end
     else
     begin {what to do if there is no number in the name?}
       fix1:=uppercase(key);
       fix2:=0;
       fix3:='';
     end;
   end;
 
{*************** ListSort ****************}
function Listsort(List:TStringList; index1,index2:integer):integer;
{Sort the "list: stringlist in customized sort order}
var
  i,n1,n2:integer;
  s1A,s2A, S1B, S2B:string;
begin
  with list do
  begin
    result:=0;
    fixkey(list[index1], S1a, N1, S1B); {split first key into 3 parts}
    fixkey(list[index2], S2a, N2, S2B); {split 2nd key into 3 parts}
    if s1a<s2a then result:=-1  {1st part of 1st key is low}
    else if s1a>s2a then result:=+1 {1st part of 1st key is high}
    else  {1st parts are equal}
    begin {compare numeric parts}
      if n1<n2 then result:=-1
      else if n1>n2 then result:=+1
      else
      begin {numeric parts also equal, compare 3rd parts}
        if S1b<s2b then result:=-1
        else if s1b>s2b then result:=+1;
       end;
    end;
  end;
end;
 
 
{************ SortBtnClick **********8}
procedure TForm1.Button1Click(Sender: TObject) ;
Var
  i:integer;
  List:TStringList;
begin
  List:=TStringlist.create;
  list.text:=Memo2.lines.text; {Move memo data into stringlist}
  list.customsort(Listsort);   {Sort the stringlist}
  Memo3.lines.Text:=list.text; {Move sorted data back to the other memo}
  list.Free;
end;
 
 
{************ FormCreate **********8}
procedure TForm1.FormCreate(Sender: TObject);
var list:TStringList;
begin
  list:=TStringList.Create;
  list.Text:=memo2.Lines.Text;
  list.Sort; {resort "properly"sorted input list back to dictionary order
              so we can test the custom sort}
  memo2.Lines.Text:=list.text;
  list.free;
end;
 
 
procedure TForm1.StaticText1Click(Sender: TObject);
begin
  ShellExecute(Handle, 'open', 'http://www.delphiforfun.org/',
  nil, nil, SW_SHOWNORMAL) ;
end;
 
 
end.


which also works and is not dependant on any external DLLs.

Many thanks,

Gerald.
Antonio Tortosa...

Posts: 6
Registered: 1/12/08
Re: Sorting numbers as strings  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 9, 2017 1:11 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Gerald Holdsworth wrote:

I've noticed that both Windows and OSX sort these (in Explorer/Filer)
in the order above

That is because they know they are displaying filenames and will
perform hueristics on them, which includes extracting the numbers from
the filenames and using the numbers during the sorting algorithm.

Also see:

The sort order for files and folders whose names contain numerals is
different in Windows Vista, Windows XP, and Windows Server 2003 than it
is in Windows 2000

https://support.microsoft.com/en-us/help/319827/the-sort-order-for-files-and-folders-whose-names-contain-numerals-is-d

but a TStringList.Sort will sort them thus

Because it doesn't know anything about the numbers encoded in the
filenames, it is just sorting the string characters as-is.

Is there some easy trick to sort them as Windows and Mac OS does?

Since you know the format of your filenames, you can use
TStringList.CustomSort() and do your own parsing, for example:

function SortFileNames(List: TStringList; Index1, Index2: Integer):
Integer;
var
  Num1, Num2: Integer;
begin
  Num1 := StrToInt(ChangeFileExt(ExtractFileName(List[Index1]), ''));
  Num2 := StrToInt(ChangeFileExt(ExtractFileName(List[Index2]), ''));
  Result := Num2 - Num1;
end;
 
MyList.CustomSort(SortFileNames);


Or, on Windows only, you can use the Win32 StrCmpLogicalW() function:

https://msdn.microsoft.com/en-us/library/bb759947.aspx

function StrCmpLogicalW(psz1, psz2: PWideChar): Integer; stdcall;
external 'Shlwapi.dll';
 
function SortFileNames(List: TStringList; Index1, Index2: Integer):
Integer;
begin
  Result := StrCmpLogicalW(PChar(List[Index1]), PChar(List[Index2]));
end;
 
MyList.CustomSort(SortFileNames);


--
Remy Lebeau (TeamB)

Hi, you can also use the new comparation options available on the string helper, those are multiplatform and support comparing strings with numbers inside as numeric values.

Un saludo, Antonio Tortosa.
Gerald Holdsworth

Posts: 76
Registered: 5/23/12
Re: Sorting numbers as strings  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 9, 2017 2:36 AM   in response to: Antonio Tortosa... in response to: Antonio Tortosa...
Antonio Tortosa Botella wrote:
Hi, you can also use the new comparation options available on the string helper, those are multiplatform and support comparing strings with numbers inside as numeric values.
How would one go about using these?

Cheers,

Gerald.
Antonio Tortosa...

Posts: 6
Registered: 1/12/08
Re: Sorting numbers as strings  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 30, 2017 4:56 AM   in response to: Gerald Holdsworth in response to: Gerald Holdsworth
Gerald Holdsworth wrote:
Antonio Tortosa Botella wrote:
Hi, you can also use the new comparation options available on the string helper, those are multiplatform and support comparing strings with numbers inside as numeric values.
How would one go about using these?

Hi Gerald, have you tried: http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.SysUtils.TStringHelper.Compare

Cheers,

Gerald.

Un saludo, Antonio Tortosa.
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02