Vor kurzem wurde ich durch den Beitrag
http://www.delphipraxis.net/1181723-post35.html und insbesondere durch den Kommentar
Juhu, es klappt, ich könnte tanzen...
auf die diesem Beitrag
http://www.delphipraxis.net/1181699-post21.html angehängte "NatCompare.pas" aufmerksam.
Leider kann ich XxnemesisxX49s Begeisterung überhaupt nicht teilen, weil die
Unit mehrere Fehler enthält.
Im Detail:
Wenn einer der zu vergleichenden Strings leer ist, kommt es vor, dass eine
Exception "xxx ist kein Integerwert" auftritt.
Ursache :
In
function GetNext(const S: String; var Start: Integer; var IsNumber: Boolean): String;
wird zwar erkannt, dass "nichts mehr da ist", was verglichen werden könnte, jedoch wird IsNumber nicht = false gesetzt, was in der aufrufenden Funktion
NaturalCompareFunc dazu führen kann, dass versucht wird einen leeren String mit IntToStr in einen Integerwert umzuwandeln.
Abhilfe :
In
GetText als erste Zeile
IsNumber:=false; einfügen.
Wenn beide Strings an der gleichen Position Zahlen enthalten, und eine dieser Zahlen größer als maxint ist, wird ebenfalls eine
Exception geworfen.
Abhilfe:
In der Funktion
NaturalCompareFunc die Zeile
Result := StrToInt(P1) - StrToInt(P2)
ersetzen durch
result:=NumCompare(P1,P2)
Die weiter unten stehende
NumCompare verarbeitet auch kilometerlange Zahlen.
Die in den Funktionen
NaturalCompareStr und
NaturalCompareText den Stringvergleichen nachgeschaltete Funktion
IntSign ist fehlerhaft.
Delphi-Quellcode:
function IntSign(x:integer):Integer;
begin
if x > 0 then
Result := 1
else if x < 0 then
Result := -1;
end;
Was passiert wohl, wenn x = 0 ist ?
Richtig, das Resultat ist undefiniert, zumindest unter XE2 wenn die Optimierung ausgeschaltet ist. Bei anderen Versionen mag das anders sein. Auf jeden Fall sollte man sich nicht darauf verlassen, dass auch bei zukünftigen Versionen das EAX-Register unverändert bleibt. Ein weiteres Argument ist, dass bei 64-Bit Versionen der Parameter "x" in RCX übergeben wird, das Resultat aber weiterhin in RAX erwartet wird. Fairerweise muss man aber zugestehen, dass diese Entwicklung bei Erstellung des Codes wohl nicht absehbar war.
Abhilfe :
Als erste Zeile
Result:=x;
einfügen.
Delphi-Quellcode:
// Vergleicht die numerischen Werte zweier Strings.
// Achtung : Die aufrufende Funktion muss sicherstellen, dass S1 und S2 nicht leer sind
// und ausschließlich aus Zeichen "0".."9" besteht.
FUNCTION NumCompare(const S1,S2:String):integer;
var n1,n2:integer; p1,p2:PChar;
begin
p1:=@s1[1];
p2:=@s2[1];
while p1^='0' do inc(p1);
while p2^='0' do inc(p2);
n1:=@s1[Length(s1)]-p1;
n2:=@s2[Length(s2)]-p2;
result:=n1-n2;
if (result<>0) or (n1<0) and (n2<0) then exit;
repeat
result:=Ord(p1^)-Ord(p2^);
inc(p1);
inc(p2);
until (result<>0) or (p1^=#0);
end;