[gelöst] Daten aus Record mit Dyn.Array in Datei speichern

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Benutzeravatar
willi4willi
Lazarusforum e. V.
Beiträge: 155
Registriert: Sa 1. Nov 2008, 18:06
OS, Lazarus, FPC: Windows, Linux (debian) / Lazarus 3.2 / FPC 3.2.2
CPU-Target: i386, win64, arm

[gelöst] Daten aus Record mit Dyn.Array in Datei speichern

Beitrag von willi4willi »

Ich habe ein einfaches Programm, das Daten in eine Datei schreibt und wieder liest.
Es funktioniert auch, wie es soll, aber es lässt sich nicht mehr beenden.

Das Wort "Ende" schreibt es noch und dann bleibt es hängen.

Code: Alles auswählen

 
program project1;
uses sysutils;
 
Type
  TBoolArray = array of boolean;
  TLongWordArray = array of longword;
 
  TSpeicher = Record
               Inputs : TBoolarray;
               Outputs : TBoolarray;
               Counters : TLongWordArray;
              end;
 
var Daten1, Daten2 : TSpeicher;
 
    i: Integer;
    f: THandle;
 
begin
  f:=FileCreate('Daten1.dat');
  If F=-1 then Halt(1);
 
  setlength(Daten1.Inputs,1);
  setlength(Daten1.Outputs,1);
  setlength(Daten1.Counters,4);
 
  setlength(Daten2.Inputs,1);
  setlength(Daten2.Outputs,1);
  setlength(Daten2.Counters,4);
 
  Daten1.Inputs[0]:=TRUE;
  Daten1.Outputs[0]:=TRUE;
  Daten1.Counters[0]:=1;
  Daten1.Counters[1]:=2;
  Daten1.Counters[2]:=3;
  Daten1.Counters[3]:=4;
 
 
  FileWrite(F,Daten1,SizeOf(Daten1));
  FileClose(F);
 
 
  F:=FileOpen ('Daten1.dat',fmOpenRead);
  If F=-1 then Halt(1);
  FileRead(F,Daten2,SizeOF(Daten2));
  FileClose(F);
 
  Writeln(Daten1.Inputs[0]:10,Daten1.Outputs[0]:10);
  Writeln(Daten2.Inputs[0]:10,Daten2.Outputs[0]:10);
  For i:=0 to 3 do Write(Daten1.Counters[i]:10); Writeln;
  For i:=0 to 3 do Write(Daten2.Counters[i]:10); Writeln;
 
//  Readln;
 
// das hier habe ich nachträglich eingefügt, da ich die dyn. Arrays in Verdacht hatte - ohne Erfolg
  setlength(Daten1.Inputs,0);
  setlength(Daten1.Outputs,0);
  setlength(Daten1.Counters,0);
  setlength(Daten2.Inputs,0);
  setlength(Daten2.Outputs,0);
  setlength(Daten2.Counters,0);
 
  Daten1.Inputs:=nil;
  Daten1.Outputs:=nil;
  Daten1.Counters:=nil;
  Daten2.Inputs:=nil;
  Daten2.Outputs:=nil;
  Daten2.Counters:=nil;
 
  Writeln('Ende');
end.
 
 


Als Fehlermeldung kommt ein Exception-Klasse External: SIGSEGV. Bei Adresse 409ACE :roll:

Debugger ausschalten brachte nichts. Array mit fester Länge funktioniert, ist aber keine Lösung. Auch die Array-Größe auf null setzen brachte nichts.

Ich verwendete Windows 10 und Laz. 1.8.4 (FPC 3.0.4) bzw. 1.9.0 (FPC 3.1.1).

Hat jemand eine Idee, was ich falsch mache?
Zuletzt geändert von willi4willi am Mo 3. Dez 2018, 17:36, insgesamt 1-mal geändert.
 

Viele Grüße

Willi4Willi

------------

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

Re: Daten aus Record mit Dyn.Array in Datei speichern

Beitrag von wp_xyz »

Dynamische Arrays sind wie Strings letztendlich Pointer - leider ist im Gegensatz zum klassichen Pascal heuzutage das Pointer-Häkchen oft nicht mehr nötig, so dass man nicht immer erkennt, ob eine Variable ein Pointer ist oder nicht. Da dein Record TSpeicher drei dynamische Arrays enthält, schreibst du also die drei Pointer in die Datei, aber nicht die Daten worauf sie zeigen. Und wenn du später die Datei einliest, werden die Pointer wieder gelesen - nur zeigen sie irgendwohin, auf keinen Fall auf die erwarteten Daten.

Ein dynamisches Array musst du so in die Datei schreiben, dass zunächst die Array-Länge geschrieben wird, denn die Leseroutine weiß das sonst nicht. Anschließend werden die Array-Elemente geschrieben, trivialerweise in einer Schleife Element für Element, aber es geht auch schneller indem du in der Schreibprozedur das erste Element angibst und dann die gesamte Byte-Größe aller Elemente, was sich errechnet als Länge des Array mal Größe jedes Elements - die Elemente stehen ja unmittelbar hintereinander im Speicher - auf diese Weise werden alle Elemente auf einmal geschrieben.

Code: Alles auswählen

// Schreib-Routine
var
  n: Integer;
begin
  n := Length(Daten1.Inputs);
  FileWrite(F, n, SizeOf(n));
  FileWrite(F, Daten1.Inputs[0], n*SizeOf(boolean));
  n := Length(Daten1.Outputs);
  FileWrite(F, n, SizeOf(n));
  FileWrite(F, Daten1.Outputs[0], n*SizeOf(boolean));
  n := Length(Daten1.Counters);
  FileWrite(F, n, SizeOf(n));
  FileWrite(F, Daten1.Counters[0], n*SizeOf(LongWord));

Zum Lesen musst du in exakt derselben Reihenfolge vorgehen: Länge lesen, Speicher reservieren per SetLength, Elemente einlesen

Code: Alles auswählen

// Lese Routine
var
  n: Integer;
begin
  FileRead(F, n, Sizeof(n));
  SetLength(Daten2.Inputs, n);
  FileRead(F, Daten2.Inputs[0], n*SizeOf(boolean));
 
  FileRead(F, n, SizeOf(n));
  SetLength(Daten2.Outputs, n);
  FileRead(F, Daten2.Outputs[0], n*SizeOf(boolean));
 
  FileRead(F, n, SizeOf(n));
  SetLength(Daten2.Counters, n);
  FileRead(F, Daten2.Counters[0], n*SizeOf(LongWord));
...

Das kann man sicher durch spezielle Schreib/Leseprozeduren vereinfachen, aber ich will hier nur das Prinzip zeigen.

Benutzeravatar
willi4willi
Lazarusforum e. V.
Beiträge: 155
Registriert: Sa 1. Nov 2008, 18:06
OS, Lazarus, FPC: Windows, Linux (debian) / Lazarus 3.2 / FPC 3.2.2
CPU-Target: i386, win64, arm

Re: [gelöst] Daten aus Record mit Dyn.Array in Datei speiche

Beitrag von willi4willi »

Es hat so funktioniert.

Ich dachte der Übergang vom statischen zum dynamischen Array wäre, einfach die Größe wegzulassen und zur Laufzeit mit setlength festzulegen.
Aber wenn die Variable nur den Zeiger speichert, dann erklärt das alles.

Danke!
 

Viele Grüße

Willi4Willi

------------

Antworten