Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi length(s) = 0 ODER s = '' ? (https://www.delphipraxis.net/65042-length-s-%3D-0-oder-s-%3D.html)

BlueStarHH 11. Mär 2006 12:58


length(s) = 0 ODER s = '' ?
 
Was sollte man besser benutzen, um zu prüfen ob ein String leer ist?
length(s) = 0
oder
s = ''

Ich würde auf der erste tippen, da dies ein Vergleich von zwei Integerwerten ist, der wohl schneller abläuft als ein String-Vergleich. ODer gibt es keinen Unterschied da der Compiler s = '' automatisch in eine performante Entsprechung umwandelt?

alcaeus 11. Mär 2006 13:02

Re: length(s) = 0 ODER s = '' ?
 
Ich wuerde auf zweiteres tippen, schliesslich muss beim ersten ja die Laenge berechnet werden. ;)

Greetz
alcaeus

Muetze1 11. Mär 2006 13:05

Re: length(s) = 0 ODER s = '' ?
 
Ich würde auf ersteres tippen, schliesslich weiss ein AnsiString um seine Länge.

marabu 11. Mär 2006 13:09

Re: length(s) = 0 ODER s = '' ?
 
Ich würde einen Haltepunkt setzen und dort das CPU-Fenster öffnen.

Grüße vom marabu

KingIR 11. Mär 2006 13:12

Re: length(s) = 0 ODER s = '' ?
 
Folgender kleiner Test:
Delphi-Quellcode:
var
  str1, str2: String;
  StartTime: array[0..1] of Cardinal;
  EndTime: array[0..1] of Cardinal;
  i: integer;
begin
  str1 := '';
  str2 := 'Hallo, das ist ein kleiner, wenngleich doch etwas längerer Test-String.';

  StartTime[0] := GetTickCount();
  for i := 1 to 1000000000 do
  begin
    if str1 = '' then
      ;
    if str2 = '' then
      ;
  end;
  EndTime[0] := GetTickCount();

  StartTime[1] := GetTickCount();
  for i := 1 to 1000000000 do
  begin
    if Length(str1) = 0 then
      ;
    if Length(str2) = 0 then
      ;
  end;
  EndTime[1] := GetTickCount();

  ShowMessage('Vergleich mit leerem String: ' + FloatToStr((EndTime[0] - StartTime[0]) / 1000) + ' Sekunden,'#13#10 +
    'Vergleich über Length(): ' +  FloatToStr((EndTime[1] - StartTime[1]) / 1000) + ' Sekunden');
Ergebnis (ohne Gewähr):
Vergleich mit leerem String: etwa 1,5 bis 1,8 Sekunden
Vergleich über Length(): etwa 4 bis 4,3 Sekunden

:angel:

Muetze1 11. Mär 2006 13:15

Re: length(s) = 0 ODER s = '' ?
 
Ok, die Haltepunktmethode sagt es am besten aus:

Bei einem Vergleich auf einen leeren String erzeugt der Compiler einen Code der direkt und sofort nachschaut ob der String leer ist (Vergleich ob <> Nil, da EmptyStr im Normalfall Nil ist).

Bei der Abfrage mit Length ruft er wirklich noch LStrLen auf - welcher zwar sofort selbiges feststellt und zurück kehrt, aber er ruft die Funktion nunmal auf - was es langsamer macht.

Also ein Vergleich auf einen Leerstring ist ein "Einzeiler" - somit schneller.

Angel4585 11. Mär 2006 20:25

Re: length(s) = 0 ODER s = '' ?
 
kommt auf den Verwendungszweck an, mein Vorschlag:

Length(Trim(s)) = 0

falls nämlich ein Leerzeichen drin steht das aber genauso als leer angesehen werden soll wird das gleich mit abgezogen und auch ' ' gilt dann als leer

BlueStarHH 11. Mär 2006 20:55

Re: length(s) = 0 ODER s = '' ?
 
Zitat:

Zitat von Angel4585
kommt auf den Verwendungszweck an, mein Vorschlag:

Length(Trim(s)) = 0

falls nämlich ein Leerzeichen drin steht das aber genauso als leer angesehen werden soll wird das gleich mit abgezogen und auch ' ' gilt dann als leer

Wie Du aus den oberen Postings erkennen kannst, sollte statt Length besser = '' benutzt werden. In Deinem Fall also Trim(s) = '' wenn man es wirklich braucht. Trim bei jedem Vergleich einfach so einzufügen halte ich für nicht sinnvoll.

negaH 11. Mär 2006 20:56

Re: length(s) = 0 ODER s = '' ?
 
dann ist

if Trim(s) = '' then

denoch schneller als

if Length(Trim(S)) = 0 then

Length() ist nunmal ein normaler Funktionsaufruf und der einfache Vergleich = '' eher als Compilermagic anzusehen.

Gruß Hagen

dizzy 12. Mär 2006 03:37

Re: length(s) = 0 ODER s = '' ?
 
Drüber hinaus ist ein String mit einem Leerzeichen streng genommen nicht als leer anzusehen. Das ist dann schon wieder vom Kontext abhängig. Keinesfalls aber eine allgemein zu treffende Aussage ;)

Master_RC 12. Mär 2006 04:32

Re: length(s) = 0 ODER s = '' ?
 
Nun gut, jetzt haben wir schon herausgefunden, was schneller ist (if s = ''), aber was ist sicherer?

Angel4585 12. Mär 2006 07:55

Re: length(s) = 0 ODER s = '' ?
 
nun das kommt darauf an was das Ganze bezwecken soll.. wenn nu ein Namensfeld hast wo Buchstaben drin stehen sollen und ein einfaches Leerzeichen ohne was dazu als leer zu betrachten ist dann ist trim schon die richtige variante, wenn nicht ist es egal was du nimmst, es dürfte alles genauso sicher sein.

jbg 12. Mär 2006 08:43

Re: length(s) = 0 ODER s = '' ?
 
Zitat:

Zitat von Master_RC
aber was ist sicherer?

Unter Delphi.Win32 ist beides gleich sicher, denn es ist nicht ohne Trickserei möglich, eienen String "mit" ohne Zeichen zu bekommen. Denn konvertiert man einen PChar in einen String, macht die Compilermagic automatisch NIL daraus, wenn der PChar leer ist. Wie folgender Code zeigt:
Delphi-Quellcode:
function GetP: PChar;
begin
  Result := #0;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  S: string;
begin
  S := GetP;
  if Pointer(S) = nil then
    ShowMessage('OK');
end;
Und bei einer Zuweisung von '' an eine String-Variable, wird die Variable automatisch auf NIL gesetzt.

Und wenn S = '' nicht immer funktionieren würde, hätte ich ein ziemlich großes Problem, was ich nicht habe.

negaH 13. Mär 2006 04:41

Re: length(s) = 0 ODER s = '' ?
 
was ist mit

Delphi-Quellcode:

var
  S: String;
begin
  S := 'A';
  S[1] := #0;

  if S <> '' then ;

  SetLength(S, 1);
  S[1] := #0;

end;
Gruß Hagen

thomasw 13. Mär 2006 05:25

Re: length(s) = 0 ODER s = '' ?
 
Mich wundert, dass der Compiler das nicht erkennt und entsprechend optimiert...

Robert Marquardt 13. Mär 2006 06:55

Re: length(s) = 0 ODER s = '' ?
 
Das kann nicht optimiert werden. Es ist ja explizit ein Puffer von 1 Zeichen fuer den String angefordert worden.
Der code funktioniert aber natuerlich. Es geht nur der initiale interne Test auf nil nicht. Der folgende String-Compare ergibt natuerlich wieder einen Leerstring.

SirThornberry 13. Mär 2006 06:58

Re: length(s) = 0 ODER s = '' ?
 
da es sich hierbei um einen String handelt würde "Length()" nicht 0 zurück geben (weil eben ein #0) vorhanden ist und der Vergleich mit "= ''" würde auch fehlschlagen weil eben etwas in dem String ist. Anders würde es bei einem PChar aussehen aber darum geht es ja hier nicht.

jbg 13. Mär 2006 09:01

Re: length(s) = 0 ODER s = '' ?
 
Zitat:

Zitat von thomasw
Mich wundert, dass der Compiler das nicht erkennt und entsprechend optimiert...

Was soll er denn da optimieren? Bei der ersten Zuweisung "S := #0" wird ein String generiert, der das #0-Zeichen enthält, also Length(S) = 1. StrLen(PChar(S)) hingegen würde 0 liefern, weil ein PChar auf #0 endet. Ein AnsiString/WideString endet aber nicht bei #0, sondern er endet da, wo der Puffer zu Ende ist, und das ist nach dem #0-Zeichen, was also eine Länge von 1 ergibt.
Das ist eben ein kleiner, aber feiner Unterschied zwischen PChar und String.

Unter .NET ist das aber wieder was anderes, da es dort in C# einen Unterschied macht, ob man S=NULL oder S="" schreibt. Ersteres wird als S:=nil interpretiert. Zweiteres hingegen als ein gültiges Leer-String Objekt. Man muss in .NET also immer auf beides prüfen: if (S != NULL && S != "") wenn man sicher gehen will, dass kein Null-String/Leer-String verarbeitet wird. Die Length() Funktion von Delphi fängt beide Fälle ab. In C# hingegen kracht es, wenn man S.length() aufruft, und S==NULL ist.

himitsu 13. Mär 2006 10:22

Re: length(s) = 0 ODER s = '' ?
 
Zitat:

Zitat von alcaeus
Ich wuerde auf zweiteres tippen, schliesslich muss beim ersten ja die Laenge berechnet werden. ;)

Greetz
alcaeus

Hab jetzt nicht alles durchgelesen, aber die Länge muß nicht erst berechnet werden, denn im String-Header steht die Länge schon drin.

S = '' und S <> '' sind dennnoch die schneller, denn diese sind intern nur Vergleiche auf
Pointer(S) = nil und Pointer(S) <> nil, wärend Length ein Funktionsaufrus ist, wo also nicht nur Verglichen, sondern ja auch erstmal zu der Funktion hin und wieder zurückgesprungen werden muß.

Wobei Length(S) = 0 also in etwas deinem Aufruf folgender Funktion gleicht:

Delphi-Quellcode:
Function Length(Const S: String): Integer;
  Begin
    If Pointer(S) = nil Then Begin
      Result := 0;
      Exit;
    End;
    Result := PInteger(@S - 4)^;
  End;
Was auch gleich klären sollte, warum der Aufruf von Length(Trim(S)) = 0 langsamer als nur Length(S) = 0 sein sollte, denn Trim ist ja 'ne StringOperation, we´lche intern selbst neben anderen Funktionsaufrufen das Length aufruft und nebenbei eventuell sogar noch Speicher reserviert, welche nach Length wieder freigegeben wird, außerden sind dazu auch noch Kopieroperationen im RAM zu beobachten, wenn etwas in S drin ist.


Ach ja, etwas zur inneren Funktion der Delphi-Strings/-Arrays:Theorätisch ist ein String auch nur ein dynamisches Array, was wiederum einem Pointer auf eine bestimmte Datenstrucktur entspricht.
Wobei diese Strucktir rein theoretisch ein Array mit der Länge 0 sein könnte, aber es wurde so gerägelt, das in diesem Fall das 0-Längen-Array freigegeben wird und der Pointer auf nil steht, weßhalb es S immer nil sein sollte.
Solange niemand absichtlich gegen diese Regel verstößt, was ich aber bisher noch nicht erlebt hab ... was aber auch sinlos wäre, da es sonst du netten Fehlern in vielen/ nahezu allen Delphifunktionen kommen würde, da diese Daten erwarten, wenn S <> nil ist ... es würde dann einfach, ohne ein weiteres Prüfen, ob die Längenangabe wirklich auf größer 0 steht auf den "nichtvorhandenen" Speicherbereich zugegriffen ;)


Und S := #0 ist ein String mit der Länge von einem Zeichen, wobei dieses Zeichen eben ein #0 ist ... in Strings wird eben nur auf die Längenangabe geachtet und ein #0 wird nicht als Stringende angesehen, weßhalb ich ja auch gerne mal einen String zur einfachen Aufnahme von Binärdaten verwende, da dem ja die #0 nichts anhaben kann :roll:


PS: die obrigen Angaben beziehen sich af ein natives Delphi und nicht auf .net.

runger 13. Mär 2006 10:39

Re: length(s) = 0 ODER s = '' ?
 
Hallo

Delphi-Quellcode:
  SetLength(S, 1);
  S[1] := #0;
eigentlich ist diese Anweisungskombination im eigentlichen Sinne nicht gültig.
Auf setlength, im Allgemeinen, bezogen gibts hier das Element S[1] garnicht.
Das ist bei Strings (in Delphi) anders hier besteht ein String aus einer Längenangabe
gefolgt von dem ersten Zeichen. Streng genommen erzeugt setlength(S,1) einen leeren
String mit der Länge 0.

Rainer

Muetze1 13. Mär 2006 11:26

Re: length(s) = 0 ODER s = '' ?
 
Nein, wieso sollte? Das Längenbyte an Index 0 bei einem String wird nur noch emuliert und der Zugriff darauf verboten. Bei einem ShortString kann man aber gerne noch darin rumpfuschen.

SetLength(s, 1); erzeugt einen String mit einer Länge von 1 - d.h. der Index 1 ist gültig und somit nutzbar. Das Längenbyte an Index 0 wird simuliert und existiert ausserhalb der Regel und ist eh nicht von SetLength() betroffen. SetLength() bei Strings setzt direkt die Länge und da Strings 1-basierend sind, ist es ein String der Länge von 1 Zeichen.

Im Gegensatz zur Nutzung von dynamischen Array's die tatsächlich immer nullbasierend sind.

himitsu 14. Mär 2006 12:27

Re: length(s) = 0 ODER s = '' ?
 
Genaugenommen wird mir SetLength(S, 1) ein Array mit der Länge 2 erstellt, da dem String aus Kompatibilitätsgründen zu PChar noch eine #0 anhängt wird.

Und das bei LongStrings (AnsiString, WideString) ebenfalls mit der Zählung bei 1 beginnen, obwohl es das Längenbyte nicht mehr gibt, sollte ebenfalls allen bekannt sein - ist ja wegen der Kompatitär zu den "alten" ShortStrings.

Hier nochmal die genauen Definitionen der Stringtypen:
Delphi-Quellcode:
Type TShortStringInfo = packed Record
    Length: Byte;                                       <<< @VarOfThisType
    Chars: packed Array[1..Length(thisString)] of Char;
  End;

  TDynArrayInfo = packed Record
    //MemoryInfo: LongInt;
    RefCount:    LongInt;
    ElementCount: LongInt;
    Data:        packed Array[0..High(thisArray)] of {ArrayTyp}; <<< @VarOfThisType
  End;

  TAnsiStringInfo = packed Record
    //MemoryInfo: LongInt;
    RefCount:    LongInt;
    ElementCount: LongInt;
    Data:        packed Array[1..Length(thisString)] of AnsiChar; <<< @VarOfThisType
    StringEnd:   AnsiChar; < ist immer #0
  End;
Code:
@ShortString        = TShortStringInfo/TShortStringInfo.Length
@DynArray           = TDynArrayInfo.Data/TDynArrayInfo.Data[0]
@String/@AnsiString = TAnsiStringInfo.Data/TAnsiStringInfo.Data[1]
.MemoryInfo gehört eigentlich zum MemoryManager, aber da für jedes Array ein eigener Speicherblock reservert wird, ist das halt "immer" an dieser Stelle :roll:

Angel4585 14. Mär 2006 12:30

Re: length(s) = 0 ODER s = '' ?
 
:lol: eigentlich war die ursprüngliche Frage ja nur ob Length(s) = 0 oder s = '' :wall:

himitsu 14. Mär 2006 15:44

Re: length(s) = 0 ODER s = '' ?
 
Es geht auch kurz, :tongue:

s = ''

oder wenn man unbedingt will

Pointer(s) = nil

NicoDE 14. Mär 2006 15:50

Re: length(s) = 0 ODER s = '' ?
 
Zitat:

Zitat von runger
Streng genommen erzeugt setlength(S,1) einen leeren
String mit der Länge 0.

Eher einen String der Länge 1 mit undefiniertem Inhalt...

himitsu 15. Mär 2006 11:02

Re: length(s) = 0 ODER s = '' ?
 
Zitat:

Zitat von NicoDE
Eher einen String der Länge 1 mit undefiniertem Inhalt...

Mit der Länge stimme ich dir ja "fast" zu .. die #0 :zwinker:

Aber wenn der String vorher länger als "1" war, dann ist der Inhalt sehr wohl definiert, er besteht nämlich aus dem ersten Zeichen der Daten, welche vorher drin waren :roll:


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:16 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz