Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi TFileStream mit dynamischen Array (https://www.delphipraxis.net/29875-tfilestream-mit-dynamischen-array.html)

Fischli80 15. Sep 2004 23:08


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 :-(

Thebe 15. Sep 2004 23:27

Re: TFileStream mit dynamischen Array
 
Du musst statt record oben packed record benutzen.

Manzoni 15. Sep 2004 23:58

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 ?

dizzy 16. Sep 2004 01:24

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

Chewie 16. Sep 2004 09:51

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;

dizzy 16. Sep 2004 11:58

Re: TFileStream mit dynamischen Array
 
Zitat:

Zitat von Chewie
richtet packed in der Standardeinstellung an 4 Byte- und nicht an 8 Byte-Grenzen aus

:oops: Dann hab ich wohl mal gefummelt, ohne zurück zu fummeln...

Zitat:

Zitat von Chewie
und zweitens hat das hier mit dem Problem nichts zu tun.

Mit den Pointern hast du völlig Recht. Das hab ich da mal spontan übersehen, und das wird hier wohl das Hauptproblem sein. Allerdings ist es trotzdem eine gute Idee packed zu benutzen, da hier auch einige Byte-Werte vorkommen, die dann nicht auf 4 Bytes gepaddet werden müssten, und beim Write nach jedem Byte 3 Byte nullen nach sich zögen. :angel2:

mfg, dizzy

Fischli80 16. Sep 2004 14:08

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?

Chewie 16. Sep 2004 15:02

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:
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;
Wenn du nun einen Array of Record benutzt, musst du beim Lesen und schreiben halt die Länge des Arrays mit Sizeof(RecordTyp) multiplizieren.
Soweit verständlich?

Ach ja, eine Variante, ein dynamisches Arrays so in ein statisches umzuwandeln, kenn ich leider nicht.

Fischli80 20. Sep 2004 13:34

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:
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;
und nun meine 2 Funktionen:

Delphi-Quellcode:
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;
Der Compiler liefert bei mir folgende Fehler bezüglich der Zeilen f.Read(c.entries^.... und f.WriteBuffer(cl.entries^.....

Zitat:

Fehler: Zeigertyp erwartet
Fehler: Inkompatible Typen
Fehler: Operator auf diesem Operandentyp nicht anwendbar
Ausserdem mußte ich den Befehl WriteBuffer nehmen, da bei WriteBlock die Antwort kam dies sei eine unbekannte Funktionen.

Hab ich was missverstanden?

Chewie 20. Sep 2004 13:50

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:
f.WriteBuffer(Pointer(cl.entries)^, Length(cl.entries) * Sizeof(CurrListEntry));
Analog beim Lesen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:27 Uhr.

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