Einzelnen Beitrag anzeigen

Benutzerbild von GPRSNerd
GPRSNerd

Registriert seit: 30. Dez 2004
Ort: Ruhrpott
171 Beiträge
 
Delphi XE4 Professional
 
#4

AW: Zwei Version-Strings (mit Buchstaben) vergleichen

  Alt 17. Feb 2011, 13:09
Hier mal ein Versuch, der sich auf numerische Strings (z.B. "1234.1224.1234.1234") mit Separator (Char, default ".") beschränkt.
Die Anzahl der Teilstrings und deren nummerische Größe ist nur limitiert durch die Größe von Int64, da keine Summenzahl aus den Teilstrings gebildet wird, sondern alle Päarchen einzeln miteinander verglichen werden, bis ein eindeutiges Ergebnis feststeht.

Delphi-Quellcode:
type
  //Returntype für Versionsnummern vergleichen
  TValueRelationship = -1..1;

const
  //Returnvalues für Versionsnummern vergleichen
  LessThanValue = Low(TValueRelationship);
  EqualsValue = 0;
  GreaterThanValue = High(TValueRelationship);

function IsNumeric(const s: String): Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := 1 to Length(s) do
    if not CharInSet(s[i], ['0'..'9']) then exit;
  Result := True;
end;

function CompareVersionStrings(const V1, V2: string;
                               const Separator: char = '.'): TValueRelationship;
var
  iV1, iV2: Int64;
  slV1, slV2: TStringList;
  i: Integer;
begin
  Result := EqualsValue; //temp

  //Übergebene Parameter validieren
  if ((V1='') or (V2='') or (Separator='')) then
  begin
    Raise Exception.CreateFmt('CompareVersionStrings: Leere Parameter nicht erlaubt ("%S", "%S", "%S")', [V1, V2, Separator]);
    Exit;
  end;

  slV1 := TStringList.Create;
  slV2 := TStringList.Create;
  try

    //Strings in Teilstrings mit Stringlists zerlegen
    slV1.Delimiter := Separator;
    slV1.DelimitedText := V1;
    slV2.Delimiter := Separator;
    slV2.DelimitedText := V2;
    if ((slV1.Count<>slV2.Count) or (slV1.Count=0) or (slV2.Count=0)) then
    begin
      Raise Exception.CreateFmt('CompareVersionStrings: Anzahl der Teilstrings ist nicht identisch (%D bei "%S", %D bei "%S")', [slV1.Count, V1, slV2.Count, V2]);
      Exit;
    end;

    //Teilstrings auswerten
    for i := 0 to slV1.Count - 1 do
    begin
      if ((slV1[i]='') or (slV2[i]='')) then
      begin
        Raise Exception.CreateFmt('CompareVersionStrings: Leere Teilstrings nicht erlaubt ("%S" bei "%S", "%S" bei "%S")', [slV1[i], V1, slV2[i], V2]);
        Exit;
      end;

      if ((not IsNumeric(slV1[i])) or (not IsNumeric(slV2[i]))) then
      begin
        Raise Exception.CreateFmt('CompareVersionStrings: Teilstrings sind nicht numerisch ("%S" bei "%S", "%S" bei "%S")', [slV1[i], V1, slV2[i], V2]);
        Exit;
      end;

      //In Integerzahlen konvertieren
      try
        iV1 := StrToInt(slV1[i]);
        iV2 := StrToInt(slV2[i]);
      except
        Raise Exception.CreateFmt('CompareVersionStrings: Konvertierung nach Integer nicht erfolgreich ("%S" bei "%S", "%S" bei "%S")', [slV1[i], V1, slV2[i], V2]);
        Exit;
      end;

      //Jetzt Integerzahlen vergleichen
      if iV1<iV2 then
      begin
        Result := LessThanValue;
        Break; //Eindeutiges Ergebnis, Schleife abbrechen
      end
      else if iV1>iV2 then
      begin
        Result := GreaterThanValue;
        Break; //Eindeutiges Ergebnis, Schleife abbrechen
      end
      else
      begin
        //Immer noch gleich, nächstes Paar vergleichen
      end;
    end;

  finally
    FreeAndNil(slV1);
    FreeAndNil(slV2);
  end;
end;
Die folgenden Positiv- und Exception-Unittests waren damit erfolgreich unter Delphi 2010:
Delphi-Quellcode:
  Assert(EqualsValue = CompareVersionStrings('0', '0'));
  Assert(EqualsValue = CompareVersionStrings('1', '1'));
  Assert(EqualsValue = CompareVersionStrings('1.2', '1.2'));
  Assert(EqualsValue = CompareVersionStrings('1.2.3', '1.2.3'));
  Assert(EqualsValue = CompareVersionStrings('1.2.3.4', '1.2.3.4'));
  Assert(EqualsValue = CompareVersionStrings('9999.9999.9999.9999', '9999.9999.9999.9999'));
  Assert(EqualsValue = CompareVersionStrings('99999.99999.99999.99999', '99999.99999.99999.99999'));
  Assert(EqualsValue = CompareVersionStrings('99999.99999.99999.99999.99999', '99999.99999.99999.99999.99999'));

  Assert(LessThanValue = CompareVersionStrings('0', '1'));
  Assert(LessThanValue = CompareVersionStrings('1', '2'));
  Assert(LessThanValue = CompareVersionStrings('1.2', '1.3'));
  Assert(LessThanValue = CompareVersionStrings('1.2.3', '1.2.4'));
  Assert(LessThanValue = CompareVersionStrings('1.2.3.4', '1.2.3.5'));
  Assert(LessThanValue = CompareVersionStrings('9991.9999.9999.9999', '9999.9999.9999.9999'));
  Assert(LessThanValue = CompareVersionStrings('9999.9991.9999.9999', '9999.9999.9999.9999'));
  Assert(LessThanValue = CompareVersionStrings('9999.9999.9991.9999', '9999.9999.9999.9999'));
  Assert(LessThanValue = CompareVersionStrings('9999.9999.9999.9991', '9999.9999.9999.9999'));
  Assert(LessThanValue = CompareVersionStrings('99999.99999.99999.99991', '99999.99999.99999.99999'));
  Assert(LessThanValue = CompareVersionStrings('99999.99999.99999.99999.99991', '99999.99999.99999.99999.99999'));

  Assert(GreaterThanValue = CompareVersionStrings('1', '0'));
  Assert(GreaterThanValue = CompareVersionStrings('2', '1'));
  Assert(GreaterThanValue = CompareVersionStrings('1.3', '1.2'));
  Assert(GreaterThanValue = CompareVersionStrings('1.2.4', '1.2.3'));
  Assert(GreaterThanValue = CompareVersionStrings('1.2.3.5', '1.2.3.4'));
  Assert(GreaterThanValue = CompareVersionStrings('9999.9999.9999.9999', '9991.9999.9999.9999'));
  Assert(GreaterThanValue = CompareVersionStrings('9999.9999.9999.9999', '9999.9991.9999.9999'));
  Assert(GreaterThanValue = CompareVersionStrings('9999.9999.9999.9999', '9999.9999.9991.9999'));
  Assert(GreaterThanValue = CompareVersionStrings('9999.9999.9999.9999', '9999.9999.9999.9991'));
  Assert(GreaterThanValue = CompareVersionStrings('99999.99999.99999.99999', '99999.99999.99999.99991'));
  Assert(GreaterThanValue = CompareVersionStrings('99999.99999.99999.99999.99999', '99999.99999.99999.99999.99991'));

  Assert(EqualsValue = CompareVersionStrings('1,2', '1,2', ','));
  Assert(LessThanValue = CompareVersionStrings('1,2', '1,3', ','));
  Assert(GreaterThanValue = CompareVersionStrings('1,3', '1,2', ','));
  Assert(GreaterThanValue = CompareVersionStrings('9999#9999#9999#9999', '9999#9999#9999#9998', '#'));

  Vergleich: TValueRelationship;
  //Exceptions: Leere Parameter
  try
    Vergleich := CompareVersionStrings('1', '');
  except
  end;
  try
    Vergleich := CompareVersionStrings('', '2');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1', '2', #0);
  except
  end;

  //Exceptions: Ungleiche Counts
  try
    Vergleich := CompareVersionStrings('1.2.3', '1.2.3.4');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2.3.4', '1.2.3');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2.3', '1.2..4');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2..4', '1.2.3');
  except
  end;

  //Exceptions: Leere Teilstrings
  try
    Vergleich := CompareVersionStrings('1.2..4', '1.2.3.4');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2.3.4', '1.2..4');
  except
  end;

  //Exceptions: Nichtnumerisch
  try
    Vergleich := CompareVersionStrings('1.2.3A', '1.2.3');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2.3', '1.2.3B');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2.3A', '1.2.3B');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2.3.A', '1.2.3.4');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2.3.4', '1.2.3.B');
  except
  end;
  try
    Vergleich := CompareVersionStrings('1.2.3.A', '1.2.3.B');
  except
  end;
//Edit: Exceptionhandling angepasst nach Tests.
Stefan

Geändert von GPRSNerd (17. Feb 2011 um 16:48 Uhr)
  Mit Zitat antworten Zitat