<?xml version="1.0" encoding="UTF-8"?>        <rss version="2.0"
             xmlns:atom="http://www.w3.org/2005/Atom"
             xmlns:dc="http://purl.org/dc/elements/1.1/"
             xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
             xmlns:admin="http://webns.net/mvcb/"
             xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
             xmlns:content="http://purl.org/rss/1.0/modules/content/">
        <channel>
            <title>
									Lazarus Pascal - Natural Sort string compare function - Delphi, Lazarus, Free Pascal				            </title>
            <link>https://www.tweaking4all.com/forum/delphi-lazarus-free-pascal/lazarus-pascal-natural-sort-string-compare-function/</link>
            <description>Tweaking4All.com Discussion Board</description>
            <language>en-US</language>
            <lastBuildDate>Tue, 21 Jul 2026 02:04:28 +0000</lastBuildDate>
            <generator>wpForo</generator>
            <ttl>60</ttl>
							                    <item>
                        <title>Lazarus Pascal - Natural Sort string compare function</title>
                        <link>https://www.tweaking4all.com/forum/delphi-lazarus-free-pascal/lazarus-pascal-natural-sort-string-compare-function/#post-6198</link>
                        <pubDate>Mon, 22 Dec 2025 13:15:47 +0000</pubDate>
                        <description><![CDATA[The biggest issue with sorting text strings is that simple string comparison won&#039;t cut it for us humans. (Wiki)
For example, let&#039;s say we have this list:
file 3.txt
file 2.txt
file 10.tx...]]></description>
                        <content:encoded><![CDATA[<p>The biggest issue with sorting text strings is that simple string comparison won't cut it for us humans. (<a href="https://en.wikipedia.org/wiki/Natural_sort_order" target="_blank" rel="noopener">Wiki</a>)</p>
<p>For example, let's say we have this list:</p>
<pre contenteditable="false">file 3.txt
file 2.txt
file 10.txt
file 9.txt
file 1.txt
</pre>
<p>Regular sorting would produce this:</p>
<pre contenteditable="false">file 1.txt
file 10.txt
file 2.txt
file 3.txt
file 9.txt
</pre>
<p>But that is not how we humans sort, we'd like to see (called "natural sort"):</p>
<pre contenteditable="false">file 1.txt
file 2.txt
file 3.txt
file 9.txt
file 10.txt</pre>
<p>In case you have seen my QuickSort examples (for example <a href="https://www.tweaking4all.com/forum/delphi-lazarus-free-pascal/lazarus-pascal-quicksort-tstringlist-to-quicksort-array-of-records/" target="_blank" rel="noopener">this one</a> - I'll add the code below), you'll see that we often use "<a href="https://www.freepascal.org/docs-html/3.2.0/rtl/sysutils/stricomp.html" target="_blank" rel="noopener">StrIcomp</a>" to compare 2 strings to see which one goes first. Unfortunately results in the wrong output I just showed. So I wrote a "drop in" replacement for this "NaturalCompare". Simply replace the StrIcomp with NaturalCompare and the QuickSort will now produce a natural sorted list.</p>
<pre contenteditable="false">function NaturalCompare(const S1, S2: string): Integer;
var
  I, J: Integer;
  N1, N2: Int64;
  C1, C2: Char;
begin
  I := 1;
  J := 1;

  while (I &lt;= Length(S1)) and (J &lt;= Length(S2)) do
  begin
    C1 := S1;
    C2 := S2;

    // If both characters are digits, compare numbers
    if (C1 in ) and (C2 in ) then
    begin
      N1 := 0;
      while (I &lt;= Length(S1)) and (S1 in ) do
      begin
        N1 := N1 * 10 + Ord(S1) - Ord('0');
        Inc(I);
      end;

      N2 := 0;
      while (J &lt;= Length(S2)) and (S2 in ) do
      begin
        N2 := N2 * 10 + Ord(S2) - Ord('0');
        Inc(J);
      end;

      if N1 &lt;&gt; N2 then
        Exit(N1 - N2);
    end
    else
    begin
      // Case-insensitive character compare
      C1 := UpCase(C1);
      C2 := UpCase(C2);

      if C1 &lt;&gt; C2 then
        Exit(Ord(C1) - Ord(C2));

      Inc(I);
      Inc(J);
    end;
  end;

  // If all equal so far, shorter string comes first
  Result := Length(S1) - Length(S2);
end;                                                           </pre>
<p>&nbsp;</p>
<p>Example QuickStort procedure:</p>
<pre contenteditable="false">procedure QuickSort(var A: TStringList);
  procedure Sort(L, R: Integer);
  var
    I, J: Integer;
    Y, X:string;
  begin
    I:= L; J:= R; X:= A;

    repeat
      // was:  while StrIcomp(pchar(A),pchar(X))&lt;0 do inc(I);
      // was:  while StrIComp(pchar(X),pchar(A))&lt;0 do dec(J); 

      while NaturalCompare(pchar(A),pchar(X))&lt;0 do inc(I);
      while NaturalCompare(pchar(X),pchar(A))&lt;0 do dec(J); 

      if I &lt;= J then
        begin
          Y:= A; A:= A; A:= Y;
          inc(I); dec(J);
        end;
    until I &gt; J;

    if L &lt; J then Sort(L,J);
    if I &lt; R then Sort(I,R);
  end;
begin
  Sort(0,A.Count-1);
end;</pre>]]></content:encoded>
						                            <category domain="https://www.tweaking4all.com/forum/delphi-lazarus-free-pascal/">Delphi, Lazarus, Free Pascal</category>                        <dc:creator>Hans</dc:creator>
                        <guid isPermaLink="true">https://www.tweaking4all.com/forum/delphi-lazarus-free-pascal/lazarus-pascal-natural-sort-string-compare-function/#post-6198</guid>
                    </item>
							        </channel>
        </rss>
		