![]() |
TFileStream mit dynamischen Array
Habe folgenden Sachverhalt:
Delphi-Quellcode:
type x = record
x1 : string [255]; x2 : string [40]; x3 : string [80]; x4 : string [50]; x5 : string [4]; x6 : byte; end; y = record y1 : string [255]; y2 : string [80]; y3 : string [40]; y4 : string [20]; y5 : byte; y6 : byte; y7 : byte; y8 : byte; end; z = record z1 : x; z2 : array of y; end; var myZ : z; procedure saveZToFile; var zf : TFileStream; begin fz := TFileStream.Create ('test.dat', fmCreate); fz.WriteBuffer (myZ, SizeOf(myZ)); fz.free; end; procedure loadZFromFile; var zf : TFileStream; begin fz := TFileStream.Create ('test.dat', fmOpenRead); fz.ReadBuffer (myZ, SizeOf(myZ)); fz.free; end; Mein Problem ist nun, dass der aktuelle Datensatz nicht richtig gespeichert wird, wobei anscheinend die SizeOf Methode nicht die richtige Größe ermittelt. Durch testen ist mir aufgefallen, dass es damit zusammenhängt, dass das Array dynamisch ist bzw. wenn Strings nicht in der Länge terminiert sind. Aber zur Laufzeit ist myZ genau bestimmt und das Array mittels SetLength auch begrenzt auf wenige Datensätze. Wie kann ich nun dieses Konstrukt speichern und laden??? Was hab ich missverstanden? Die entstehende Datei beim speichern ist 440 Byte groß also ca die Größe von x. Hoffe Ihr könnt mir helfen :-( |
Re: TFileStream mit dynamischen Array
Du musst statt record oben packed record benutzen.
|
Re: TFileStream mit dynamischen Array
sicher? hab zwar keine Ahnung wo sein Problem ist, aber packed bezieht sich doch nur auf eine Komprimierung im Speicher, oder ?
|
Re: TFileStream mit dynamischen Array
Keine Kompression, sondern die eingestellte Ausrichtung wird übergangen, und jedes Element beginnt mit packed direkt nachdem das vorige endet. Ohne packed werden die Elemente an 8 Byte (Standardeinstellung - kann geändert werden) ausgerichtet, so das ein Element nicht z.B. am 4. Byte der Struktur beginnen kann, sondern immer nur in 8 Byte-Schritten.
Das ist/war eine Geschwindigkeitsfrage, und auch zur Kompatibilität zu anderen Sprachen/Settings. Mal abgesehen vom x86 haben viele CPUs sonst auch Probleme mit nicht ausgerichteten Feldern - so wohl auch der Athlon64. Den kann man damit richtig schön ärgern und langsam machen ;). x86er korrigieren das intern. (afaik!) Zum Schreiben ist packed hingegen sehr praktisch, da ein Datentyp der nicht genau an einer Grenze aufhört so nicht mit Nullen aufgefüllt werden muss (Padding), und dann im File doch etwas "schöner" aussieht :). gruss, dizzy |
Re: TFileStream mit dynamischen Array
Mal langsam hier, Leute. Erstens richtet packed in der Standardeinstellung an 4 Byte- und nicht an 8 Byte-Grenzen aus und zweitens hat das hier mit dem Problem nichts zu tun.
Das Problem ist vielmehr, dass dynamische Array- und lange String-Variablen (auch Recordfelder) Zeiger auf den dafür allokierten Heap-Speicher sind, das Array bzw. der String ist also nicht direkt im Record enthalten. Um den Record dann sebst in der Datei zu speichern, muss man die Zeiger dann selbst dereferenzieren. Eine Möglichkeit wäre z.B, das so zu machen:
Delphi-Quellcode:
type
TMyRec = record Data1: Integer; Count: Integer; Vals: Array of Integer; end; var fs: TFileStream; rec: TMyRec; begin fs := TFileStream.Create({wie gehabt}); rec.Count := Length(rec.Vals); fs.WriteBlock(rec, Sizeof(Rec) - Sizeof(Pointer)); fs.WriteBlock(rec.Vals^, Length(Vals) * Sizeof(Integer)); fs.Free; end; |
Re: TFileStream mit dynamischen Array
Zitat:
Zitat:
mfg, dizzy |
Re: TFileStream mit dynamischen Array
@Chewie
Erklärung scheint mir allgemein sehr plausibel. Aber könntest du zu meinem besseren Verständnis noch ein paar Worte verlieren zu dem was dein Quellcode versucht mir zu sagen? rec.Count gibt also die tatsächliche Länge des Arrays an oder? Bei mir ist das auch wieder ein Record. Macht das nen Unterschied? Und wenn ich deinen Programmablauf umkehren möchte, sozusagen wieder laden, wie muss dass dann aussehen, damit wieder ein vernünftiger Record bei rauskommt? Bin wahrscheinlich zu verwirrt um die Tomaten von den Augen zu bekommen :-) Und gibt es nicht eine Variante ein dynamisches Array zur Laufzeit fixieren zu lassen, quasi eine Möglichkeit es in ein statisches zu verwandeln? |
Re: TFileStream mit dynamischen Array
rec.Count gibt die Länge des Arrays an, ist also vor dem Schreiben identisch mit Length(Array). Ich benutz das nur, damit ich später beim Einlesen die Länge des Arrays weiß, denn die brauch ich ja. Den Code fürs Einlesen schreib ich dir jetzt, hatte vorhin keine Zeit mehr.
Delphi-Quellcode:
Wenn du nun einen Array of Record benutzt, musst du beim Lesen und schreiben halt die Länge des Arrays mit Sizeof(RecordTyp) multiplizieren.
type
TMyRec = record Data1: Integer; Count: Integer; Vals: Array of Integer; end; var fs: TFileStream; rec: TMyRec; begin fs := TFileStream.Create({wieauchimmer}); fs.Read(rec, Sizeof(rec) - Sizeof(Pointer)); //wir lesen die ersten beiden Felder ein SetLength(rec.Vals, rec.Count); //wir setzen die Anzahl der Elemente auf den //Wert, den wir eben eingelesen haben fs.Read(rec.Vals^, rec.Count * Sizeof(Integer)); //nun lesen wir den Inhalt des Arrays ein fs.Free; end; Soweit verständlich? Ach ja, eine Variante, ein dynamisches Arrays so in ein statisches umzuwandeln, kenn ich leider nicht. |
Re: TFileStream mit dynamischen Array
Erstmal Danke für die Hilfe bisher. Ich glaub ich habs bald.
Aber so ganz klappts noch ni bei mir. Hier mal mein Quellcode: ** Typdefinitionen **
Delphi-Quellcode:
und nun meine 2 Funktionen:
type
currListData = record s1 : string [255]; s2 : string [80]; s3 : string [40]; s4 : string [50]; s5 : string [4]; b1 : byte; end; currListEntry = record s1 : string [80]; s2 : string [40]; s3 : string [20]; s4 : string [255]; b1 : byte; b2 : byte; b3 : byte; b4 : byte; end; currList = record data : currListData; count : integer; entries : array of currListEntry; end;
Delphi-Quellcode:
Der Compiler liefert bei mir folgende Fehler bezüglich der Zeilen f.Read(c.entries^.... und f.WriteBuffer(cl.entries^.....
function loadCurrList (fn : string; var cl:currList):boolean;
var f : TFileStream; begin loadCurrList := true; try f := TFileStream.Create (fn, 0); f.Read(cl, Sizeof(cl) - Sizeof(Pointer)); SetLength(cl.entries, cl.Count); f.Read(cl.entries^, cl.Count * Sizeof(currListEntry)); f.free; except loadCurrList := false; end; end; function saveCurrList (fn : string; cl : currList):boolean; var f : TFileStream; begin saveCurrList := true; try f := TFileStream.Create(fn, fmCreate); cl.Count := Length(cl.entries); f.WriteBuffer(cl, Sizeof(cl) - Sizeof(Pointer)); f.WriteBuffer(cl.entries^, Length(entries) * Sizeof(CurrListEntry)); f.Free; except saveCurrList := false; end; end; Zitat:
Hab ich was missverstanden? |
Re: TFileStream mit dynamischen Array
Einmal hast du nur entries anstelle von cl.entries geschrieben. Und die beiden anderen Fehler rühren wohl daher, dass der Compiler es nicht schnallt, dass ein dyn. Array ein Zeiger ist. Versuch also noch den Cast auf Pointer:
Delphi-Quellcode:
Analog beim Lesen.
f.WriteBuffer(Pointer(cl.entries)^, Length(cl.entries) * Sizeof(CurrListEntry));
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:36 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