Vorkommastellen

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
benderboxer
Beiträge: 12
Registriert: Sa 21. Dez 2019, 20:55

Vorkommastellen

Beitrag von benderboxer »

Hallo Zusammen!

Ich habe das Problem, dass ich nicht weiß und nicht herausfinde, wie man aus einer 1 → 01 macht

Ich möchte ein Output geben wo vorne eine Nummerierung ist. Aber da hapert es.

Was muss ich wo hinzufügen, dass das folgendermaßen aussieht:

01
02
03
04
05
06
07
08
09
10
11
...


Danke im Vorraus!

Grüße
Ben

wp_xyz
Beiträge: 4889
Registriert: Fr 8. Apr 2011, 09:01

Re: Vorkommastellen

Beitrag von wp_xyz »

Ich nehme an, dass 1, 2, 3, ... in deinem Beispiel Integer sind. Diese kannst du mit der FormatFloat-Funktionen in einen String umwandeln, der wie gewünscht aussieht, wenn du die Formatmaske '00' verwendest. Jede '0' ist dabei ein Platzhalter, der durch die Ziffern der zu wandelnden Zahl ersetzt wird, oder unverändert bleibt, wenn die Zahl dort keine Ziffer hat. Wenn die Zahl mehr Stellen hat als die Maske, werden die zusätzlichen Ziffern zusätzlich in den Ausgabestring aufgenommen.

Also: Aus 1 wird '01', aus 12 wird '12', aus 135 wird '135'.

Code: Alles auswählen

program Project1;
 uses
  SysUtils;
var
  i: Integer;
begin
  for i:=0 to 12 do
    WriteLn(FormatFloat('00', i));
  WriteLn(FormatFloat('00', 135));
  ReadLn;
end.

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Vorkommastellen

Beitrag von Winni »

Hi!

Oder selber machen:

Code: Alles auswählen

 
function fillLeadingZeros ( Value, len : integer) : string ;
begin
result := IntToStr(Value);
while length(result) < len do result := '0'+result;
end;


Kurz und knapp.

Winni

Warf
Beiträge: 1910
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Vorkommastellen

Beitrag von Warf »

Winni hat geschrieben:Hi!

Oder selber machen:

Code: Alles auswählen

 
function fillLeadingZeros ( Value, len : integer) : string ;
begin
result := IntToStr(Value);
while length(result) < len do result := '0'+result;
end;


Kurz und knapp.

Winni


Nein das willst du nicht machen. Mal ganz davon abgesehen das das ganze nicht korrekt ist (beispiel fillLeadingZeros (-2, 3) wird zu 0-2), macht dein Code string konkatination im loop, was ein echter performance Killer ist.
Der Grund dafür ist das folgende, bei jeder String konkatination passiert das:

Code: Alles auswählen

function ConcatStrings(const str1: string; const str2: String): String;
begin
  SetLength(Result, Str1.Length + Str2.Length);
  Move(str1[1], Result[1], str1.length);
  Move(str2[1], Result[str1.length], str2.length);
end;

Das alloziieren des neuen speichers ist sehr teuer (im vergleich dazu sind die beiden Moves vernachlässigbar), denn der Memory-Manager führt eine First-Fit suche über den Speicher aus. Der FPC Memory Manager hat den Vorteil das er nicht so einfach fragmentiert, was bedeutet das nach einer gewissen laufzeit das ganze nicht extremst langsam wird (was ein vorteil für solche konkatinationen ist), allerdings braucht es trozdem zeit, und selbst auf einem nicht fragmentierten systemm willst du das wenn möglich vermeiden, und vor allem nicht in einem loop machen. Wenn möglich willst du lieber direkt von anfang an die korrekte menge Speicher alloziieren
Wenn du etwas hinten dran hängen würdest, wäre das nicht ganz so schlimm, da dann der string nur extended werden müsste, was bedeutet das du nur setlength + den zweiten move ausführen müsstest, da sich der anfang ja nicht ändert. Da du aber vorn anhängst, muss zu erst der speicher erweitert werden, dann der inhalt nach hinten gezogen werden, und schließlich der neue Inhalt vorne hin kopiert werden muss.

Das ist aber alles nicht notwendig, die länge eines integers ist Ceil(log10(value+1)) und damit effizient berechenbar (zumindest effizienter als jede memoryoperation, und eine integer log10 funktion lässt sich im notfall auch noch effizienter bauen als die float funktion die math bietet). Leider bietet der FPC keine inplace konvertierungsfunktion wie C's itoa funktion an (natürlich könnte man gegen die libc linken, aber damit hat man mehr probleme als das es hilft), aber FPC bietet die Str Funktion, welche einen String mit einer mindestlänge returned. Basteln wir also damit mal die Funktion:

Code: Alles auswählen

function FillLeadingZerosStr(Value: Integer; len: Integer): String; inline;
var
  i: Integer;
begin
  Str(Value:len, Result);
  for i:=1 to len do
    if Result[i] = ' ' then
      Result[i] := '0'
    else
      break;
end;


Wohlgemerkt die Funktion ist immernoch genauso kaputt wie deine funktion (also -2, 3 gibt 0-2), aber fast 3 mal so schnell

Das ist aber nur die Lösung wenn Geschwindigkeit relevant ist, dann sollte man es halt selbst machen (aber dann auch nicht mit sinnlosen String Konkatinationen, sondern richtig).
Wenn Geschwindigkeit nicht relevant ist, tuts auch der Einzeiler mit Format. Wenn man keine floats braucht kann man auch den folgenden Format-Befehln nutzen:

Code: Alles auswählen

Result := Format('%.*d', [len, Value]);

Damit wird die Zahl value auf len stellen gepadded. Mit Format ist ein gutes stück langsamer (etwa 80% langsamer als deine Funktion) dafür aber ist man flexibler als mit einer eigens geschriebenen Funktion (grade wenn man es auf Floats mit FormatFloat erweitern will), und es hat natürlich den Charme das es ein Einzeiler ist.

Je nachdem für was man es braucht lohnt es sich das ganze selbst zu schreiben, aber in den allermeisten Fällen ist Format nicht nur ausreichend, sondern mit der Flexibilität und der Robustheit (wie gesagt, deine Funktion ist allein schon kaputt), oftmals genau das was man will

Antworten