Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   String vorne mit Nullen auffüllen (https://www.delphipraxis.net/211405-string-vorne-mit-nullen-auffuellen.html)

Walter Landwehr 11. Sep 2022 12:26

String vorne mit Nullen auffüllen
 
Hallo ich möchte ein String mit variabler Länge immer bis zur einer bestimmten Länge vorne mit 0 auffüllen. habe dazu folgende Function:
Delphi-Quellcode:
function FillString(AString: String; AChar: Char; ALength: integer): String;
var i:integer;
begin
  FillChar(Result, ALength, Ord(AChar));
  Result := copy(AString, 1, ALength) + copy(Result, ALength - Length(AString), ALength);
end;
aufrufen der Function:
Delphi-Quellcode:
BelegNr := FillString(BelegNr,'0',9);
leider bekomme ich beim Aufruf der function eine Exception. Exception-Klasse $C0000005 mit Meldung 'access violation at 0x0040e4c6: read of address 0x3030302c

was ist da falsch?

TigerLilly 11. Sep 2022 12:56

AW: String vorne mit Nullen auffüllen
 
https://docwiki.embarcadero.com/Libr...Helper.PadLeft

Rolf Frei 11. Sep 2022 13:52

AW: String vorne mit Nullen auffüllen
 
1. Es gibt dafür bereits existierende Funktionen, die das machen.
2. In deinem Code schreibst du in einen nicht allozierten Speicher und erhälst dadurch die AV. Den Result String musst du zuerst entsprechend allozieren, bevor du da ein FillChar machst. Also einfach ein SetLength(Result, ALength) davor einfügen.
3. Mach aus dem ersten Parameter eine Const Parameter. Das solltest du für allen String Parameter machen, die du in der Funktinon nicht veränderst. Ohne const wird der String bei jedem Aufruf der Funktion kopiert, während er bei einem const als Pointer übergeben wird und die Zeit für das Kopierren gespart wird.
Delphi-Quellcode:
function FillString(const AString: String; AChar: Char; ALength: integer): String;
var i:integer;
begin
  SetLength(Result, ALength);
  FillChar(Result, ALength, Ord(AChar));
  Result := copy(AString, 1, ALength) + copy(Result, ALength - Length(AString), ALength);
end;

Walter Landwehr 11. Sep 2022 14:26

AW: String vorne mit Nullen auffüllen
 
Hallo Rolf,

habe die Function geändert aber die Exception kommt immer noch.

mytbo 11. Sep 2022 15:02

AW: String vorne mit Nullen auffüllen
 
Wenn es keine fertige Funktion gibt, dann:
Delphi-Quellcode:
function FillStringLeft(const pmcSource: String; pmChar: Char; pmStrLen: Integer): String;
begin
  if Length(pmcSource) >= pmStrLen then
    Result := pmcSource
  else
    Result := Result.Create(pmChar, pmStrLen - Length(pmcSource)) + pmcSource;
end;

var
  sBelegNr: String;
begin
  sBelegNr := '12345';
  ShowMessage(FillStringLeft(sBelegNr, '0', 9));
Bis bald...
Thomas

Uwe Raabe 11. Sep 2022 15:16

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von Walter Landwehr (Beitrag 1511597)
Delphi-Quellcode:
  FillChar(Result, ALength, Ord(AChar));

Obwohl es FillChar heißt, werden da nicht Char gefüllt sondern Byte. Da Char heutzutage 2 Byte groß sind, füllt der Aufruf nur die erste Hälfte - und das auch noch ab der falschen Stelle (0 statt 1 -> Bang!). Bei Nullen könnte das sogar noch klappen, aber bei echten Unicode-Zeichen würde auch das Ord(AChar) nur das untere Byte berücksichtigen. Fazit: FillChar für Strings ist blöd.

KodeZwerg 11. Sep 2022 15:39

AW: String vorne mit Nullen auffüllen
 
oder total oldschool:
Delphi-Quellcode:
function FillString(AString: String; AChar: Char; ALength: integer): String;
var i:integer;
  s: string;
begin
  if Length(AString) > ALength then
    Exit(AString);
  s := '';
  for i := 0 to Pred(ALength - Length(AString)) do
    s := s + AChar;
  Result := s + AString;
end;

DieDolly 11. Sep 2022 15:42

AW: String vorne mit Nullen auffüllen
 
Weiß nicht mehr woher ich das habe, aber hier ein Einzeiler
Delphi-Quellcode:
function AddLeadingZeroes(const aNumber, aFinalLength: Integer): string;
begin
 Result := System.SysUtils.Format('%.*d', [aFinalLength, aNumber]);
end;

himitsu 11. Sep 2022 15:44

AW: String vorne mit Nullen auffüllen
 
Ja, hier ist nicht das Delphi-Char gemeint, sondern das Char der C-Welt und dort heißt es "Byte".
Bissl verwirrend.

Früher hatte ich für sowas Delphi-Referenz durchsuchenStringOfChar benutzt, um die gewünschte Anzahl an zeichen zu bekommen.
Wobei, wenn BelegNr eine "Nummer" ist, dann einfach dem Delphi-Referenz durchsuchenFormat sagen, dass es füllen soll. :stupid:

[edit]
@DieDolly: jupp.
[/edit]


Delphi-Quellcode:
  S := S.PadLeft(10, '0');

  S := StringOfChar('0', 10 - Length(S)) + S;
  S := string.Create('0', 10 - S.Length) + S;

  // es geht auch andersrum, aber sowas will niemand machen
  S := Copy('0000000000', 1, 10 - Length(S)) + S;
  S := Copy('0000000000', Length(S) + 1) + S;

Walter Landwehr 11. Sep 2022 16:24

AW: String vorne mit Nullen auffüllen
 
Danke an alle jetzt funktioniert es.

Function von mytbo habe ich jetzt benutzt.

himitsu 11. Sep 2022 16:40

AW: String vorne mit Nullen auffüllen
 
@mytbo: statt Result.Create besser string.Create verwenden.

Du erzeugst einen neuen String und mit dem Result hat es eigentlich garnichts zu tun, abgesehn von dem Typ dieser Variable.

Funktionell geht es hier zwar, aber syntaktisch ist es eher fragwürdig missverständlich und in anderen Fällen auch fehleranfällig.


Zum Glück ist es hier aber kein schlimmer "Fehler".

Anders sieht es z.B. bei dem anderen syntaktisch gleichen Problem aus, wo es bei sowas ganz böse knallen kann/wird.
MyClass.Create anstatt TMyClass.Create aka Variable.Create anstatt Class.Create, weil hier kommt der Typ aus dem, auf was die Variable zeigt und nicht aus der Variablen-Deklaration selber.


@Walter:

In mytbo's Code, entspricht die Schleife dem StringOfChar bzw string.Create
und die ganze Funktion entspricht dem PadLeft.

Vorallem die Schleife, bzw. die unnötig vielen String-Operationen darin, sind eher suboptimal.

Man kann es machen, aber man kann auch die manuellen Codes durch etwas ersetzen, was es bereits fertig gibt. (vor allem, da Diese hier sogar optimaler arbeiten)

mytbo 11. Sep 2022 22:21

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von himitsu (Beitrag 1511613)
Vorallem die Schleife, bzw. die unnötig vielen String-Operationen darin, sind eher suboptimal.

Ich glaube, da ist dir ein Zuordnungsfehler bezüglich des Autors unterlaufen. In meiner Lösung gibt es keine Schleife und die in StringOfChar() ist OK.

Bis bald...
Thomas

Uwe Raabe 11. Sep 2022 22:35

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von DieDolly (Beitrag 1511609)
Delphi-Quellcode:
function AddLeadingZeroes(const aNumber, aFinalLength: Integer): string;
begin
 Result := System.SysUtils.Format('%.*d', [aFinalLength, aNumber]);
end;

Das funktioniert aber nur mit Zahlen. Der Titel spricht aber von String.

himitsu 11. Sep 2022 22:37

AW: String vorne mit Nullen auffüllen
 
Upss. (ein Zwerg)

Joar, aber der String heißt "Nummer" :lol:
OK, fast überall ist er dennoch ein String.

KodeZwerg 11. Sep 2022 23:05

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von mytbo (Beitrag 1511622)
da ist dir ein Zuordnungsfehler bezüglich des Autors unterlaufen

Bekenne mich Schuldig im Sinne der Anklage :P
Zitat:

Zitat von himitsu (Beitrag 1511624)
Upss. (ein Zwerg)

Der Zwerg kann auch ohne Schleife :lol:
Delphi-Quellcode:
function FillString(const AString: AnsiString; const AChar: AnsiChar; const ALength: Integer): AnsiString;
var
  Len: Integer;
begin
  Len := ALength - Length(AString); // padding "berechnen"
  if (Len > 0) then // kurzer check
    begin
      SetLength(Result, ALength); // länge setzen
      FillChar(Result[1], Len, AChar); // padding einfügen
      Move(AString[1], Result[Succ(Len)], Length(AString)); // zusammenfügen
    end
    else
      Result := AString; // das original so belassen
end;
Rein historisch betrachtet ist es sehr schnell.
Ich bete das mir da kein Fehler unterlaufen ist :twisted:

Rolf Frei 12. Sep 2022 13:30

AW: String vorne mit Nullen auffüllen
 
Das mit dem Fillchar und dem String war mir beim schreiben ganicht aufgefallen, ist aber natürlich korrekt. Fillchar darf hier nicht verwendet werden. Da gibt es eine andere Funktion "StringOfChar()" dafür:

Delphi-Quellcode:
function FillString(const AString: String; AChar: Char; ALength: integer): String;
var i:integer;
begin
  Result := StringOfChar(AChar, ALength);
  Result := copy(AString, 1, ALength) + copy(Result, ALength - Length(AString), ALength);
end;

Dennis07 12. Sep 2022 13:47

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von KodeZwerg (Beitrag 1511625)
Der Zwerg kann auch ohne Schleife :lol:

Najaaaa, wenn du die Schleife einfach nach "Move" auslagerst, dann ja. :-D

Zitat:

Zitat von KodeZwerg (Beitrag 1511625)
Delphi-Quellcode:
function FillString(const AString: AnsiString; const AChar: AnsiChar; const ALength: Integer): AnsiString;
var
  Len: Integer;
begin
  Len := ALength - Length(AString); // padding "berechnen"
  if (Len > 0) then // kurzer check
    begin
      SetLength(Result, ALength); // länge setzen
      FillChar(Result[1], Len, AChar); // padding einfügen
      Move(AString[1], Result[Succ(Len)], Length(AString)); // zusammenfügen
    end
    else
      Result := AString; // das original so belassen
end;

Jo, aber wieso nicht einfach:
Delphi-Quellcode:
function FillString(const AString: AnsiString; const AChar: AnsiChar; const ALength: Integer): AnsiString;
var
  Len: Integer;
begin
  Len := ALength - Length(AString);
  Result := StringOfChar(AChar, Len) + AString;
end;
Das klappt dann nebenbei auch mit UnicodeString.

KodeZwerg 12. Sep 2022 13:53

AW: String vorne mit Nullen auffüllen
 
@Dennis07 Weil meine AnsiString Variante um ein dutzendfach schneller ist.

himitsu 12. Sep 2022 13:58

AW: String vorne mit Nullen auffüllen
 
per se ist AnsiString und String/UnicodeString gleich schnell, bzüglich der Speicherverwaltung, da sie intern identisch sind.

Man darf nur nicht das *2 bzw. *SizeOf(WideChar) vergessen



Wenn mann will, dann kann man in einen AnsiString auch Unicode rein tun (CP_UTF16)
und andersrum auch ANSI in einen UnicodeString (auch wenn ich Letzteres nicht empfehlen würde).

KodeZwerg 12. Sep 2022 14:02

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von Dennis07 (Beitrag 1511676)
Delphi-Quellcode:
function FillString(const AString: AnsiString; const AChar: AnsiChar; const ALength: Integer): AnsiString;
var
  Len: Integer;
begin
  Len := ALength - Length(AString);
  Result := StringOfChar(AChar, Len) + AString;
end;
Das klappt dann nebenbei auch mit UnicodeString.

Wenn Du aus AnsiString ein WideString bzw nur String machst glaube ich Dir das sofort :D

//edit
und der AnsiChar sollte dann natürlich ein WideChar bzw nur Char sein

Rolf Frei 12. Sep 2022 14:27

AW: String vorne mit Nullen auffüllen
 
Dennis07, wieso hast du den ersten Parameter auf einen AnsiString geändert? Die Funktion StringOfChar gibt es für Ansi und Unicode und somit läuft die Funktion mit dem String Typ tadellos.

Dennis07 12. Sep 2022 16:20

AW: String vorne mit Nullen auffüllen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von KodeZwerg (Beitrag 1511677)
@Dennis07 Weil meine AnsiString Variante um ein dutzendfach schneller ist.

Dass das einen signifikanten Unterschied macht bezweifle ich. Deshalb habe ich mal einen Speedtest gemacht.

Um ein ein relevantes Ergebnis zu haben habe ich beide Funktionen je 100.000 mal hintereinander mit zufälligen Strings und einem '0'-Zeichen durchlaufen lassen und habe zufällige Einrückungen im selben Spektrum genommen.
Das ganze habe ich 100 mal durchlaufen lassen und den Durchschnitt gebildet.

Die Ergebnisse siehst du im Anhang. Gerne gebe ich dir den Quelltext, damit du es selbst probieren kannst.

FillString1 ist von dir, FillString2 ist meine Variante. In dem Beispiel war deine um ca. 2 Ticken schneller bei der Aufrufzahl, was mich jetzt nicht überrascht hat. Kann aber auch an ungleichen Zufallszahlen liegen, wer weiß.
Es geht mir jetzt halt nicht darum, deinen Code schlecht zu reden oder so, nichts läge mir ferner. Aber ich wollte mich selbst davon überzeugen, dass der unter Win32 auf keinen Fall "dutzendfach langsamer" ist. Selbst, wenn meiner jetzt schneller wäre, würden wir hier von durchschnittlich zwei Ticks pro 100.000 Aufrufen sprechen, was in so ziemlich allen Anwendungsgebieten wohl kaum ins Gewicht fallen würde.

Dennis07 12. Sep 2022 16:29

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von Rolf Frei (Beitrag 1511683)
Dennis07, wieso hast du den ersten Parameter auf einen AnsiString geändert? Die Funktion StringOfChar gibt es für Ansi und Unicode und somit läuft die Funktion mit dem String Typ tadellos.

Habe ich nicht, CodeZwerg hatte das schon so, ich habe nur mal eben den Body neu geschrieben. Wie ich aber jetzt sehe, hast du ja auch eine ähnliche Lösung vorgeschlagen.

Uwe Raabe 12. Sep 2022 16:33

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von Dennis07 (Beitrag 1511700)
Kann aber auch an ungleichen Zufallszahlen liegen, wer weiß.

Ermittele mal die Zufallszahlen vorher und schreibe die in ein Array. Zum Einen bekommst du dann besser vergleichbare Ergebnisse und zum Anderen fließt die Zeit für die Random-Aufrufe nicht in die Messung mit ein.

Weiterhin würde ich TStopWatch zur Messung nehmen. Das ist potentiell genauer.

Rolf Frei 12. Sep 2022 17:14

AW: String vorne mit Nullen auffüllen
 
Zitat:

Zitat von Dennis07 (Beitrag 1511702)
Zitat:

Zitat von Rolf Frei (Beitrag 1511683)
Dennis07, wieso hast du den ersten Parameter auf einen AnsiString geändert? Die Funktion StringOfChar gibt es für Ansi und Unicode und somit läuft die Funktion mit dem String Typ tadellos.

Habe ich nicht, CodeZwerg hatte das schon so, ich habe nur mal eben den Body neu geschrieben. Wie ich aber jetzt sehe, hast du ja auch eine ähnliche Lösung vorgeschlagen.

Ah ok. Habe mich halt immer nur auf den originalen Post des Threaderstellers konzentriert und da nur die Zeile die einen AV produziert hat, geändert. Wieso da auf einmal AnsiString in das Problem des OP reinkommt ist mir rätselhaft. Die Lösung mit StringOfChar ist doch die einfachste und beste für seine Anforderung. Ich bezweifle, dass der OP diesen Code 1 Mio. mal möglichst schnell ausführen will. Selbst wenn das pro Sekunde 1000 mal gemacht wird, gibt es kaum einen nennenswerten Unterschied. Wenn dann noch die Konvertierung String zu AnsiString und retour, dazu kommt, ist vermultich der ganze Vorteil eh wieder weg, mal ganz davon abgesehen von den womöglich verlorenen Ansizeichen.

himitsu 12. Sep 2022 17:21

AW: String vorne mit Nullen auffüllen
 
Die "Einfachste" ist doch wohl eher Delphi-Referenz durchsuchenPadLeft :angle:
oder bei "Integer" ansatt String, das mit dem Format.

https://www.delphipraxis.net/211405-...ml#post1511610
https://www.delphipraxis.net/211405-...ml#post1511623


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:31 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