Entwickler-Ecke

Dateizugriff - Fehler beim Speichern von TFileStream


Bofrost_Mann - Sa 02.02.08 22:14
Titel: Fehler beim Speichern von TFileStream
Hallo, ich bin vollkommen neu auf dem Gebiet von FileStreams und möchte zwei Records in eine Datei schreiben und diese natürlich auch wieder lesen. Ich bin gerade dabei das ganze rein funktional zu testen um mit dem ganzen dann später den Datenaustausch für ein schon bestehendes Programm zu organisieren werden, was bis jetzt auf Basis von Textdateien funktioniert.

Starte ich das Programm in Delphi scheint das Speichern und Laden auch zu funktionieren, doch will ich es beenden erscheint eine Fehlermeldung:

"mit ...\Project1.exe trat ein Problem auf ... access violation at ... write of adress ...".

Starte ich die compilierte *.exe erscheint keine Fehlermeldung beim schließen, wenn die Datei mit der selpen Compilierung erstellt wurde.

Was hat es mit dieser Meldung auf sich bzw. wo liegt der Fehler?

-----

Wurde die Datei mit einer älteren Compilierung erstellt und soll sie geladen werden, erscheint eine Fehlermeldung. Die Frage hierzu: ist es möglich auch Dateien älterer Compilierungen wieder einzulesen.


Delphi-Quelltext
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:
procedure TForm1.SaveVars; //Buch in Datei schreiben
var
   Stream:TFileStream;
   SchreibBuch: array [1..2of Katalog;
begin
  SchreibBuch[1]:=Buch[1];
  SchreibBuch[2]:=Buch[2];

  Stream:=TFileStream.Create('C:\Speicher\Test.adb', fmCreate);
  try
    Stream.WriteBuffer(SchreibBuch,SizeOf(SchreibBuch));
    Stream.WriteBuffer(ProgrammVersion,SizeOf(ProgrammVersion));
  finally
    Stream.Free;
  end;  
end;
 
procedure TForm1.LoadVars; //LeseBuch aus Datei lesen
var
   Stream:TFileStream;
begin
  Stream:=TFileStream.Create('C:\Speicher\Test.adb', fmOpenRead);
  try  
    Stream.ReadBuffer(LeseBuch,SizeOf(LeseBuch));
    Stream.ReadBuffer(ProgrammVersion,SizeOf(ProgrammVersion));
  finally  
    Stream.Free;  
  end;
end;


Schonmal Danke für Eure Antworten.


Narses - Mo 04.02.08 16:58
Titel: Re: Fehler beim Speichern von TFileStream
Moin und :welcome: im Forum!

user profile iconBofrost_Mann hat folgendes geschrieben:
Was hat es mit dieser Meldung auf sich bzw. wo liegt der Fehler?

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm1.SaveVars; //Buch in Datei schreiben
var
   Stream:TFileStream;
   SchreibBuch: array [1..2of Katalog;
begin
Um deine Frage beantworten zu können, benötigen wir noch die Deklaration des Typs Katalog.

cu
Narses


Erichgue - Mo 04.02.08 17:35

Zitat:
Starte ich das Programm in Delphi scheint das Speichern und Laden auch zu funktionieren, doch will ich es beenden erscheint eine Fehlermeldung:


Was machst du denn beim Beenden?
Zeigt mal etwas Code!

Erich


Bofrost_Mann - Di 05.02.08 17:55

hier mal der komplette code der unit ... diese unit soll erstmal nur "symbolcharakter" haben und rein funktionell einwandfrei arbeiten ...


Delphi-Quelltext
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:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    Edit6: TEdit;
    Edit7: TEdit;
    Edit8: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);

  private
     procedure LoadVars; //Laden
     procedure SaveVars; //Speichern

  public
    { Public-Deklarationen }
  end;

type Katalog=record //Daten der einzelnen Personen
  Name: string;
  Vorname: string;
end;

type Version=record //Programmversion
  Programm: integer;
  Adressbuch: integer;
end;

var
  Form1: TForm1;
  Buch: array [1..2of Katalog;
  LeseBuch: array [1..2of Katalog;

  ProgrammVersion: Version;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
buch[1].name:='nachname1';
buch[1].vorname:='rufname1';

buch[2].name:='nachname2';
buch[2].vorname:='rufname2';

ProgrammVersion.Programm:=1;
ProgrammVersion.Adressbuch:=0;

edit1.text:=buch[1].name;
edit2.text:=buch[1].vorname;
edit3.text:=buch[2].name;
edit4.text:=buch[2].vorname;
end;

procedure TForm1.SaveVars; //Buch in Datei schreiben
var
   Stream:TFileStream;
   SchreibBuch: array [1..2of Katalog;
begin
  SchreibBuch[1]:=Buch[1];
  SchreibBuch[2]:=Buch[2];

  Stream:=TFileStream.Create('C:\Speicher\Test.adb', fmCreate);
  try
    Stream.WriteBuffer(SchreibBuch,SizeOf(SchreibBuch));
    Stream.WriteBuffer(ProgrammVersion,SizeOf(ProgrammVersion));
  finally
    Stream.Free;
  end;  
end;
 
procedure TForm1.LoadVars; //LeseBuch aus Datei lesen
var
   Stream:TFileStream;
begin
  Stream:=TFileStream.Create('C:\Speicher\Test.adb', fmOpenRead);
  try  
    Stream.ReadBuffer(LeseBuch,SizeOf(LeseBuch));
    Stream.ReadBuffer(ProgrammVersion,SizeOf(ProgrammVersion));
  finally  
    Stream.Free;  
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
savevars;
button1.enabled:=false;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
loadvars;

edit5.text:=LeseBuch[1].name;
edit6.text:=LeseBuch[1].vorname;
edit7.text:=LeseBuch[2].name;
edit8.text:=LeseBuch[2].vorname;
end;

end.


Keldorn - Di 05.02.08 18:18

Hallo

in deinem Record ist ein string enthalten, da wird das nix, weil die eigentliche Länge nicht bekannt ist.

Delphi-Quelltext
1:
SizeOf(SchreibBuch));                    

laß dir das mal in einer messagebox anzeigen.

Entweder, du nimmst strings mit einer festen länge

Delphi-Quelltext
1:
2:
3:
4:
type Katalog=record //Daten der einzelnen Personen  
  Name: string[30];  
  Vorname: string[30];  
end;


oder besser, such mal im Forum, Fragen zu strings und stream gibts recht häufig, oder du guggst mal nach Suche in: Delphi-Forum, Delphi-Library TREADER und Suche in: Delphi-Forum, Delphi-Library TWRITER, damit machst du dir das Leben auch leichter.

Gruß Frank


Bofrost_Mann - Di 05.02.08 21:44

Danke an alle, für Eure Tipps. Ich hab in Katalog die Strings jetzt als shortstring deklariert und bekomm auch keine fehler mehr. Das Einlesen von Dateien älterer Compilierungen scheint auch Problemlos zu funktionieren.


Bofrost_Mann - Di 05.02.08 22:24

Habe doch noch ein kleines Problem: kann ich das ganze auch dynamisch vereinbaren? D.h. ein Array mit dynamischer Länge Speichern und dann auch wieder lesen?

Folgender Versuch brachte Fehler:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TForm1.SaveVars; //Buch in Datei schreiben
var
   Stream:TFileStream;
   SchreibBuch: array of Katalog;
begin

  setlength(Schreibbuch, 3);

  SchreibBuch[1]:=Buch[1];
  SchreibBuch[2]:=Buch[2];


Narses - Di 05.02.08 22:45

Moin!

user profile iconBofrost_Mann hat folgendes geschrieben:
kann ich das ganze auch dynamisch vereinbaren? D.h. ein Array mit dynamischer Länge Speichern und dann auch wieder lesen?
Wozu überhaupt dieses Array als Umweg zum Speichern? :gruebel:

Schreib doch einfach direkt die records der Reihe nach in den Stream und gut. :nixweiss: :idea:

cu
Narses


Keldorn - Mi 06.02.08 09:59

Hallo,

wenn du nur shortstring hast, dann kommst du über die Länge des Streams und Größe deines Records auf die Anzahl der gespeicherten Daten.

Wenn du es völlig dynamisch haben willst, dann schreib als erstes die anzahl in den stream und danach dann die daten, umgedreht dann genauso.

Als Anregung dieser Thread [http://www.delphi-forum.de/viewtopic.php?t=23973&highlight=treader+records]

Gruß Frank


Bofrost_Mann - Mi 06.02.08 14:31

Hallo, und nochmal Danke an alle für die schnellen Antworten. Habe das Speichern und Laden jetzt über Treader und Twriter organisiert (thx@ Keldorn). Ich hänge den jetzigen Quelltext nocheinmal an, falls andere ähnliche Probleme haben und Anregungen suchen.

Delphi-Quelltext
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:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    Edit6: TEdit;
    Edit7: TEdit;
    Edit8: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);

  private
     procedure LoadVars; //Laden
     procedure SaveVars; //Speichern
     

  public
    { Public-Deklarationen }
  end;

type Katalog=record //Daten der einzelnen Personen
  Name: string;
  Vorname: string;
end;

type Version=record //Programmversion
  Programm: integer;
  Adressbuch: integer;
end;

var
  Form1: TForm1;
  Buch: array of Katalog;
  LeseBuch: array of Katalog;

  ProgrammVersion: Version;
  LeseProgrammVersion: Version;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
setlength(buch, 3);

buch[0].name:='';
buch[0].vorname:='';

buch[1].name:='nachname1';
buch[1].vorname:='rufname1';

buch[2].name:='nachname2';
buch[2].vorname:='rufname2';

ProgrammVersion.Programm:=1;
ProgrammVersion.Adressbuch:=0;

edit1.text:=buch[1].name;
edit2.text:=buch[1].vorname;
edit3.text:=buch[2].name;
edit4.text:=buch[2].vorname;
end;

procedure TForm1.SaveVars; //Buch in Datei schreiben
var
   Stream:TFileStream;
   SchreibBuch: array of Katalog;
   i: integer;
begin

  setlength(Schreibbuch, length(buch)); //Länge von Schreibbuch setzen

  for i:=0 to length(buch)-1 do SchreibBuch[i]:=Buch[i]; //Tiefenkopie

  Stream:=TFileStream.Create('C:\Speicher\Test.adb', fmCreate); //Datei erzeugen

  try

     begin
          with Twriter.create(Stream,1024do
               begin
                    try
                       begin
                            writeinteger(ProgrammVersion.Programm); //Programmversion schreiben
                            writeinteger(ProgrammVersion.Adressbuch);

                            writeinteger(length(schreibBuch)); //anzahl schreiben

                            for i:=0 to length(schreibBuch)-1 do
                                begin
                                     writestring(schreibbuch[i].Name);
                                     writestring(schreibbuch[i].Vorname);
                                end;
                       end;
                    finally
                           free;
                    end;
              end;
     end;

  finally

    Stream.Free;

  end;

end;
 
procedure TForm1.LoadVars; //LeseBuch aus Datei lesen
var
   Stream:TFileStream;
   i:integer;
   anzahl: integer;
begin

  Stream:=TFileStream.Create('C:\Speicher\Test.adb', fmOpenRead);

  try

    begin
        with Treader.create(Stream,1024do
             begin
                  try
                     begin
                          LeseProgrammVersion.Programm:=readinteger; //Programmversion auslesen
                          LeseProgrammVersion.Adressbuch:=readinteger;

                          anzahl:=readinteger; //anzahl lesen
                          setlength(lesebuch, anzahl);  //array-größe festlegen

                          for i:=0 to anzahl-1 do
                              begin
                                   lesebuch[i].Name:=readstring;
                                   lesebuch[i].Vorname:=readstring;
                              end;
                     end;
                  finally
                         free;
                  end;
             end;
   end;

  finally

    Stream.Free;

  end;

end;





procedure TForm1.Button1Click(Sender: TObject);
begin
savevars;
button1.enabled:=false;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
loadvars;

Form1.caption:='Kontakte Version '+inttostr(LeseProgrammVersion.Programm)+'.'+inttostr(LeseProgrammVersion.Adressbuch);

edit5.text:=LeseBuch[1].name;
edit6.text:=LeseBuch[1].vorname;
edit7.text:=LeseBuch[2].name;
edit8.text:=LeseBuch[2].vorname;
end;

end.


Liebe Grüße

Der Bofrost Mann


Narses - Do 07.02.08 12:20

Moin!

Nochmal: :roll:
user profile iconBofrost_Mann hat folgendes geschrieben:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
//...
procedure TForm1.SaveVars; //Buch in Datei schreiben
//...
  setlength(Schreibbuch, length(buch)); //Länge von Schreibbuch setzen

  for i:=0 to length(buch)-1 do SchreibBuch[i]:=Buch[i]; //Tiefenkopie
//...
                            for i:=0 to length(schreibBuch)-1 do
                                begin
                                     writestring(schreibbuch[i].Name);
                                     writestring(schreibbuch[i].Vorname);
Wozu kopierst du die Werte, hast du zuviel Speicher? :nixweiss: ;)

cu
Narses


Bofrost_Mann - Do 07.02.08 15:06

@narses: wie gesagt das Speichern und Lesen sollte nur rein funktionell einwandfrei arbeiten ... das Kopieren des Array-Records war auch nur mal für Testzwecke ... mittlerweile hab ich das Konzept in dem anderen Programm umgesetzt und dort gibts natürlich vorher keine Kopie des Arrays ...

LG Der Bofrost Mann