| Autor |
Beitrag |
Stefan S.
      
Beiträge: 184
D5
|
Verfasst: Mo 19.01.09 17:08
Hallo allerseits.
Ich arbeite an einer Speichern-/Laden-Funktion für Benutzereinstellungen, für die ich FileSteams verwende. Für Dateitypen wie Integer, Floats (Extended) oder Booleans habe ich das ganze auch bereits hinbekommen. Als es dann ans Lesen und Schreiben von Strings ging, musste ich die Forensuche bemühen und bin auf folgende Funktion/Prozedur gestoßen, die ein User gepostet hat:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| function ReadStringFromStream(F: TFileStream): String; var aStrLen : Integer; begin F.ReadBuffer(aStrLen,SizeOf(Integer)); SetLength(Result,aStrLen); if aStrLen > 0 then F.ReadBuffer(Pointer(Result)^,aStrLen); end;
procedure WriteStringToStream(F: TFileStream; aStr: String); var aStrLen : Integer; begin aStrLen:=Length(aStr); F.WriteBuffer(aStrLen,SizeOf(Integer)); if aStrLen > 0 then F.WriteBuffer(Pointer(aStr)^,aStrLen); end; |
Die habe ich dann verwendet. Nachdem ich jetzt aber auch Strings in die Datei schreibe, funktioniert gar nichts mehr, auch andere Dateitypen, die im Code vor den Strings geschrieben werden, können nicht mehr korrekt ausgelesen werden. Wahrscheinlich weil die erstellten Dateien aus irgendeinem Grund totales Chaos sind.
Hier meine Speichern-Prozedur:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56:
| procedure Speichern(Dateiname: String); var Datei: TFileStream; i: Byte; begin if FileExists(Dateiname) then Datei := TFileStream.Create(Dateiname,fmOpenWrite) else Datei := TFileStream.Create(Dateiname,fmCreate); Datei.Position := 0; Datei.WriteBuffer(UserVar_Anzahl,sizeof(UserVar_Anzahl)); if UserVar_Anzahl > 0 then begin for i := 1 to UserVar_Anzahl do with User_Variablen[i] do begin Datei.WriteBuffer(Buchstabe,sizeof(Buchstabe)); Datei.WriteBuffer(Durchlauf,sizeof(Durchlauf)); Datei.WriteBuffer(Wert,sizeof(Wert)); Datei.WriteBuffer(Start,sizeof(Start)); Datei.WriteBuffer(Ende,sizeof(Ende)); Datei.WriteBuffer(Schritt,sizeof(Schritt)); end; end; Datei.WriteBuffer(ObjektNr,sizeof(ObjektNr)); Datei.WriteBuffer(ModTrans_Anzahl,sizeof(ModTrans_Anzahl)); Datei.WriteBuffer(ViewTrans_Anzahl,sizeof(ViewTrans_Anzahl)); if ModTrans_Anzahl > 0 then begin for i := 1 to ModTrans_Anzahl do with ModTrans[i] do begin Datei.WriteBuffer(aktiv,sizeof(aktiv)); Datei.WriteBuffer(Modus,sizeof(Modus));
Datei.WriteBuffer(x.Vars,sizeof(x.Vars)); Datei.WriteBuffer(x.Wert,sizeof(x.Wert)); WriteStringToStream(Datei,x.Eingabe);
Datei.WriteBuffer(y.Vars,sizeof(y.Vars)); Datei.WriteBuffer(y.Wert,sizeof(y.Wert)); WriteStringToStream(Datei,y.Eingabe);
Datei.WriteBuffer(z.Vars,sizeof(z.Vars)); Datei.WriteBuffer(z.Wert,sizeof(z.Wert)); WriteStringToStream(Datei,z.Eingabe);
Datei.WriteBuffer(Winkel.Vars,sizeof(Winkel.Vars)); Datei.WriteBuffer(Winkel.Wert,sizeof(Winkel.Wert)); WriteStringToStream(Datei,Winkel.Eingabe); end; end; FreeAndNil(Datei); end; |
Die einzelnen Dateitypen in den Records, die ich verwende, dürften dabei nicht relevant sein, das X.Eingabe ist vom Typ ShortString.
Die Lesen-Prozedur ist im Prinzip das selbe mit ReadBuffer statt Write Buffer und der Anzahl Bytes des jeweiligen Datentyps als Parameter. ^^ Der Fehler muss irgendwie mit den Strings zu tun haben, weil es ja wie gesagt vorher funktioniert hat. Aber an der Prozedur und der Funktion aus dem Forum finde ich persönlich nichts... Kann gut sein dass es irgendein Anfängerfehler meinerseits ist, da ich in Sachen FileStreams noch nicht so bewandert bin. ^^ Moderiert von Narses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Mo 19.01.2009 um 18:16
|
|
Narses
      

Beiträge: 10185
Erhaltene Danke: 1261
W11x64
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mo 19.01.09 18:16
Moin!
Stefan S. hat folgendes geschrieben : | | Die einzelnen Dateitypen in den Records, die ich verwende, dürften dabei nicht relevant sein, das X.Eingabe ist vom Typ ShortString. |
Die Möglichkeit besteht, allerdings werden wir es erst dann sicher wissen, wenn du mal die Deklaration der Variablen zeigst, die du da in den Stream schreibst.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 19.01.09 20:00
Stefan S. hat folgendes geschrieben : | | das X.Eingabe ist vom Typ ShortString. |
Das ist bei den angegebenen Methoden zum Lesen und Schreiben des Strings ja gar nicht nötig, es können auch normale Strings beliebiger Länge (naja übertreiben muss man es nicht  ) sein.
Im Hinblick auf Delphi 2009 würde ich das aber eher so machen: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| function ReadStringFromStream(F: TFileStream): String; var aStrLen: Integer; begin F.ReadBuffer(aStrLen, SizeOf(Integer)); SetLength(Result, aStrLen); if aStrLen > 0 then F.ReadBuffer(Pointer(Result)^, aStrLen * SizeOf(Char)); end;
procedure WriteStringToStream(F: TFileStream; aStr: String); var aStrLen: Integer; begin aStrLen := Length(aStr); F.WriteBuffer(aStrLen, SizeOf(Integer)); if aStrLen > 0 then F.WriteBuffer(Pointer(aStr)^, aStrLen * SizeOf(Char)); end; | Grund: Ab Delphi 2009 ist ein Buchstabe nicht mehr unbedingt ein Byte groß. Deshalb sollte man bei der Anzahl der Bytes zum Schreiben und Lesen nicht davon ausgehen, dass jeder Buchstabe 1 Byte groß ist, sondern sollte die Größe in Bytes lieber berechnen.
Auf diese Weise funktioniert es in älteren Delphiversionen genauso wie in Delphi 2009.
Ansonsten schließe ich mich Narses an was das Problem betrifft.
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Di 20.01.09 09:59
|
|
Stefan S. 
      
Beiträge: 184
D5
|
Verfasst: Di 20.01.09 14:39
Manchmal kann es schon helfen, dass man versucht, anderen das eigene Problem zu erklären. ^^ Während ich gerade die Deklaration meiner Variablen posten wollte, bin ich selber drauf gekommen: Neben einem kleinen (aber entscheidenden) Fehler bei der Angabe der Byte-Zahl war etwas schuld, was mit den FileStreams selber wenig zu tun hatte. "ModTrans" ist ein dynamisches Array das von 0 bis X geht, die Variable "ModTrans_Anzahl" gibt dagegen die Anzahl der Elemente von 1 bis X+1 an. Da meine Schleife aber die "ModTrans"-Elemente von 1 bis "ModTrans_Anzahl" (statt von 0 bis "ModTrans_Anzahl"-1) in die Datei geschrieben hat, stand in der entsprechender Nonsens.
Wie es immer wieder diese Leichtsinnsfehler sind, die einen so lange beschäftigen... Trotzdem danke an alle für die Hilfe.
@jaenicke: OK, danke, Ratschlag berücksichtigt.
@Luckie: Hab ich auch schon überlegt. Es sind aber nicht Benutzereinstellungen, die beim Programmstart automatisch geladen werden, sie entsprechen eher einem "Projekt", das heißt der Benutzer kann beliebig viele dieser Settings anlegen. Dafür denke ich mal sind Ini-Dateien nicht gedacht.
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 20.01.09 15:09
Stefan S. hat folgendes geschrieben : | | @Luckie: Hab ich auch schon überlegt. Es sind aber nicht Benutzereinstellungen, die beim Programmstart automatisch geladen werden, sie entsprechen eher einem "Projekt", das heißt der Benutzer kann beliebig viele dieser Settings anlegen. Dafür denke ich mal sind Ini-Dateien nicht gedacht. |
Man kann auch das mit INIs lösen und mit XML-Dateien sogar sehr gut.
Bei einer guten Implementierung ist aber eine eigene Umsetzung schneller, weil man die Datei nicht parsen muss sondern gezielt bestimmte Stellen auslesen kann. Das habe ich bei einem Bibliotheksprogramm gemerkt, bei dem ich sehr viele Datensätze beim Start auslesen musste.
(Letztlich habe ich es so geschafft das Auslesen so stark zu beschleunigen, dass ich die Fortschrittsanzeige weglassen konnte, weil man die eh kaum noch sah.)
|
|
|