well I'm back. You missed me? pfffffft. I had a nasty bug in the function and engkin's comment about his routine not been able to handle empty strings slapped me in to reality, yes mine did too. Here it is corrected function EvsCompareNatural_A(const S1, S2 :AnsiString; aCaseSensitive : Boolean = True):integer;
var
vChr1, vChr2 : PChar;
vCmp1, vCmp2 : string;
function Sign(aNo:integer):integer; inline;
begin
Result := 0;
if aNo > 0 then Result := 1
else if aNo < 0 then Result := -1;
end;
function NumCharLen(const aStart:PAnsiChar):Integer;
var
vNext : PAnsiChar;
begin
vNext := aStart;
repeat
inc(vNext);
until (vNext^ = #0) or (not (vNext^ in ['0'..'9']));
Result := vNext-aStart;
end;
function CompToChar(var aStr:PAnsiChar; const aChar:Char; aCount:Integer):Integer;inline;
begin // compares the next aCount characters of aStr with aChar and returns 0 if they are all the same
Result := 0; // or <>0 if they differ. It is used to avoid padding a string with zeros.
repeat
Result := sign(Ord(aStr^) - ord(aChar));
Dec(aCount); Inc(aStr);
until (Result <> 0) or (aStr^=#0) or (aCount = 0);
end; //when checking numeric characters[0..9] against zero it always returns a positive number.
function NumComp:Integer;
var
vNl : Integer;
begin
Result := -2;
vNl := NumCharLen(vChr1) - NumCharLen(vChr2);
if vNl < 0 then Result := CompToChar(vChr2, '0', abs(vNl))
else if vNl > 0 then Result := CompToChar(vChr1, '0', vNl);
if (Result > 0) then begin
Result := Sign(vNl);
Exit;
end;
repeat
Result := sign(ord(vChr1^) - ord(vChr2^));
inc(vChr1); inc(vChr2);
until ((vChr1^=#0) or (vChr2^=#0)) //end of string has been reached
or (Result <> 0) //conclusion has been reached
or (not (vChr1^ in ['0'..'9'])) //numeric characters end here
or (not (vChr2^ in ['0'..'9'])); //numeric characters end here
if Result = 0 then begin
if vChr1^ in ['0'..'9'] then Result := 1
else if vChr2^ in ['0'..'9'] then Result := -1;
end;
end;
begin
//s1<s2 = -1, S1=S2 =0, S1>S2 = 1;
if aCaseSensitive then begin
vChr1 := @S1[1]; vChr2 := @S2[1]
end else begin
vCmp1 := LowerCase(S1); vCmp2 := LowerCase(S2);
vChr1 := @vCmp1[1]; vChr2 := @vCmp2[1];
end;
repeat
if (vChr1^ in ['0'..'9']) and (vChr2^ in ['0'..'9']) then
Result := NumComp // it exits ready in the next position
else begin
Result := Sign(ord(vChr1^)- ord(vChr2^));
if vChr1^ <> #0 then inc(vChr1);
if vChr2^ <> #0 then inc(vChr2);
end;
until (vChr1^=#0) or (vChr2^=#0) or (Result <> 0);
if (Result = 0) then Result := Sign(ord(vChr1^) - Ord(vChr2^));
end;
And on top of that I added a small speed test here is the routine I used
procedure TForm1.Button4Click(Sender :TObject);
var
vEvs :QWord;
vTypo :QWord;
vEng :QWord;
vRes :Integer;
vCntr :Integer;
vStr2, vStr1 : string; //no access delays.
begin
vStr2 := 'Callisto Morphamax 6000 SE'; vStr1 := 'Callisto Morphamax 6000 SE2';
Screen.Cursor := crHourGlass;
try
vEvs := GetTickCount64;
for vCntr := 0 to SpinEdit1.Value do
vRes := EvsCompareNatural_A(vStr2, vStr1);
vEvs := GetTickCount64 - vEvs;
vTypo := GetTickCount64;
for vCntr := 0 to SpinEdit1.Value do
vRes := StrCmpLogical(vStr2, vStr1);
vTypo := GetTickCount64 - vTypo;
vEng := GetTickCount64;
for vCntr := 0 to SpinEdit1.Value do
vRes := AnsiNaturalCompare(vStr2, vStr1);
vEng := GetTickCount64 - vEng;
StatusBar1.Panels[0].Text := 'Evs : ' + IntToStr(vEvs);
StatusBar1.Panels[1].Text := 'Typo : ' + IntToStr(vTypo);
StatusBar1.Panels[2].Text := 'Engkin : ' + IntToStr(vEng);
finally
Screen.Cursor := crDefault;
end;
end;
and attached you will find the results for a 5,000,000 iteration loop, at the statusbar.