Recent

Author Topic: Natural string comparison?  (Read 31702 times)

Dibo

  • Hero Member
  • *****
  • Posts: 1057
Natural string comparison?
« on: May 01, 2014, 11:40:42 pm »
Hi,

I'm using AnsiCompareText() function but it is not natural, instead of:

file_1.txt
file_2.txt
file_10.txt
file_20.txt

I get:
file_1.txt
file_10.txt
file_2.txt
file_20.txt

Does free pascal has natural string comparision function?

Regards

Bart

  • Hero Member
  • *****
  • Posts: 5575
    • Bart en Mariska's Webstek
Re: Natural string comparison?
« Reply #1 on: May 02, 2014, 12:02:10 am »
Not AFAIK.

While for the human eye this sorting is very easy, I think it'll be rather hard to write a generic algoritm for it.

Bart

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Natural string comparison?
« Reply #2 on: May 02, 2014, 06:11:31 am »
it is easy for you to do instead of comparing file_1.txt compare file_01.txt and so on just make sure that you add enough zeros infrond of your number to cover the biggest number you have and you are set to go. In this  case I would say that going with 4 or 5 will suffice for all your file names and the patter is easy too everything between _ and . non inclusive is a number and must be padded to the correct length before comparison.

Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Natural string comparison?
« Reply #3 on: May 02, 2014, 08:07:05 am »
If you have control over these names, then Taazz's idea would be the best. Otherwise you can use something like:
Code: [Select]
function AnsiNaturalCompare(str1, str2: string; vCaseSensitive: boolean = False): integer;
var
  l1, l2: integer; //Str length
  n1, n2: integer; //numrical part
  i1, i2: integer; //index in Str
  d: integer;
begin
  if not vCaseSensitive then
  begin
    str1 := UpperCase(str1);
    str2 := UpperCase(str2);
  end;

  l1 := Length(str1);
  l2 := Length(str2);

  i1 := 1;
  i2 := 1;
  while i1 <= l1 do
  begin
    //Compare non-numbers
    d := Ord(str1[i1]) - Ord(str2[i2]);
    if not (str1[i1] in ['0'..'9']) then
    begin
      if (d <> 0) then
      begin
        Result := d;
        exit;
      end;
    end
    else
    begin
      //Convert a section of str1 to a number
      n1 := 0;
      repeat
        n1 := 10 * n1 + Ord(str1[i1]) - Ord('0');
        Inc(i1);
      until (i1 > l1) or not (str1[i1] in ['0'..'9']);

      //Convert a section of str2 to a number
      n2 := 0;
      repeat
        n2 := 10 * n2 + Ord(str2[i2]) - Ord('0');
        Inc(i2);
      until (i2 > l2) or not (str2[i2] in ['0'..'9']);

      //Compare numbers naturally
      d := n1 - n2;
      if d <> 0 then
      begin
        Result := d;
        exit;
      end
      else
        Continue;
    end;
    Inc(i1);
    Inc(i2);
  end;
  Result := (i1 - l1) - (i2 - l2);
end;

The result is <0 if str1<str2, 0 if str1=str2, and >0 if str1>str2

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4599
  • I like bugs.
Re: Natural string comparison?
« Reply #4 on: May 02, 2014, 08:15:51 am »
I didn't know it is called "Natural string comparison" but I had a similar problem with Refdes values in electronic schematic design part lists. I made a custom datatype for sorting them.
It was simple. I first checked if a string starts with an alphabet and ends with a number. Then a parser separated the textual and numeric parts. A comparison function used them both.

You also must handle the file suffix part correctly.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1263
Re: Natural string comparison?
« Reply #5 on: May 02, 2014, 02:42:38 pm »
If you have control over these names, then Taazz's idea would be the best. Otherwise you can use something like:
...
The result is <0 if str1<str2, 0 if str1=str2, and >0 if str1>str2

Nice!  I've now nicked this routine, added it to my library and put you and this forum link down as the source.  Many thanks :)
Lazarus Trunk/FPC Trunk on Windows [7, 10]
  Have you tried searching this forum or the wiki?:   http://wiki.lazarus.freepascal.org/Alternative_Main_Page
  BOOKS! (Free and otherwise): http://wiki.lazarus.freepascal.org/Pascal_and_Lazarus_Books_and_Magazines

typo

  • Hero Member
  • *****
  • Posts: 3051
Re: Natural string comparison?
« Reply #6 on: May 02, 2014, 05:56:24 pm »

BeniBela

  • Hero Member
  • *****
  • Posts: 928
    • homepage
Re: Natural string comparison?
« Reply #7 on: May 02, 2014, 06:16:15 pm »
My bbutils have such functions  (strCompareClever, striCompareClever), too

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Natural string comparison?
« Reply #8 on: May 02, 2014, 06:25:04 pm »
Actually my idea is based on the fact that he does not have control over the names. that is why I said the pattern to extract the numbers was from _ to . not inclusive.

I like the alphanum sort link by typo which sprout an interest to see what it exists for pascal and ended up at http://objectmix.com/delphi/401713-alphanumeric-sort-routine-delphi.html which on the end of the page has a reference to StrCmpLogicalW in shlwapi.dll which should be quit helpful in windows development.

AAAAAAAnd I'm to bored to search any further at least for today.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Natural string comparison?
« Reply #9 on: May 02, 2014, 07:05:13 pm »
Actually my idea is based on the fact that he does not have control over the names.
@Taazz,
You are right. I did not pay enough attention. Your post was clear on that: "... and must be padded to the correct length before comparison."

Thanks for the information about StrCmpLogicalW in shlwapi.dll. I knew it was there somewhere but did not know where exactly and what name.

@Mike.Cornflake,
My pleasure. I give Dibo credit for the name, I had a cryptic name before.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Natural string comparison?
« Reply #10 on: May 02, 2014, 08:02:47 pm »
well I did spend some more time search after all and I found an interesting looking pascal routine for natural sorting in delphi http://objectmix.com/delphi/722211-natural-sorting-optimizing-working-solution-3.html nothing ground shaking and I do not like the fact that they convert the string to integer for comparison on top of extracting/copying the number from the original string. I'm guessing this is going to be slow and is not needed just a length comparison and if they have the same length a simple string comparison will do which should be faster from multiple divisions by 10 to get the integer out of the string.

and then there is this which I do not understand nor access.
http://www.delphipraxis.net/29910-natuerliche-sortierungen-von-strings.html
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64


Blaazen

  • Hero Member
  • *****
  • Posts: 3241
  • POKE 54296,15
    • Eye-Candy Controls
Re: Natural string comparison?
« Reply #12 on: May 02, 2014, 11:04:11 pm »
It would be nice to have it in StrUtils.
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

typo

  • Hero Member
  • *****
  • Posts: 3051
Re: Natural string comparison?
« Reply #13 on: May 02, 2014, 11:48:55 pm »
I agree.

typo

  • Hero Member
  • *****
  • Posts: 3051
Re: Natural string comparison?
« Reply #14 on: May 03, 2014, 12:47:09 am »
My test project (attached).

 

TinyPortal © 2005-2018