Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Filestream objekte auslesen


schlumsch - Sa 23.10.04 16:53
Titel: Filestream objekte auslesen
Hi,

habe einen Filestream in welchem ich beliebig viele Obj. gesaved hab ( erfolgreich :) )
Nun will ich die Daten auch wieder laden aber mir fehlt quasi die "Abbruchbedingung" für Schleife - kann mir da wer halfen?


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
   Var Stream : TFileStream;
    TmpObjekt : TToDo;
    i : byte;
  ...... 
    TmpObjekt := TToDo.create;
     Stream := TFileStream.create(Prog_Dir + '\todo.dat', fmCreate);
     Stream.position := 0;

     while Not Stream.eof do <-- Undefinierter Bezeichner: 'eof'

     begin

          Stream.WriteBuffer(TmpObjekt, sizeof(TmpObjekt));

     end;
     TmpObjekt.free;
     Stream.Free;


Kann mir wer sagen wie ich das bewerkstellige?

Thx,

Schlumsch


Delete - Sa 23.10.04 17:17


Delphi-Quelltext
1:
while fs.pos > fs.size do                    


Neidhard von Reuental - Sa 23.10.04 17:24

mmm, meinst du nicht das das auslesen der verschiedenen objekte aus einem stream problematisch werden könnte wenn du nicht weißt wo eine anfängt und endet?
oder speicherst du jedes objekt in eine eigene datei?


schlumsch - Sa 23.10.04 19:02

Also danke erstmal. Funktioniert schonmal allerdings ohne den gewünschten Effekt.
Beim speichern wird die Datei angelegt und auch etwas gespeichert ... was auch immer das sein mag. Das laden der Objekte geht auch nur beim Zugriff auf eine der Eigenschaften bekomm ich `ne Fehlermeldung da die Eigenschaften alle leer sind (was sie vorher nicht waren).
... kann sich da mal bitte wer anschauen was ich da "verreisse"?? danke


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure TAlarmform.todo_liste_speichern;
Var Stream : TFileStream;
    TmpObjekt : TToDo;
    i : byte;
begin
 if Listbox_todo.Items.Count <> 0 then
 begin
     TmpObjekt := TToDo.create;
     Stream := TFileStream.create(Prog_Dir + '\todo.dat', fmCreate);
     Stream.position := 0;
     for i := 0 to (Listbox_todo.items.count - 1) do
     begin
          TmpObjekt := TToDo(Listbox_todo.items.Objects[i]);
          Stream.WriteBuffer(TmpObjekt, sizeof(TmpObjekt));
     end;
     TmpObjekt.free;
     Stream.Free;
 end;
end;




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:
procedure TAlarmform.todo_liste_laden;
Var Stream : TFileStream;
    s : string;
    TmpObjekt : TToDo;
begin
 if fileexists(Prog_Dir + '\todo.dat') then
 begin
 try
     showmessage('datei da');
     Stream := TFileStream.create(Prog_Dir + '\todo.dat', fmOpenRead);
     Stream.position := 0;
    while Stream.position < Stream.size do
     begin
     TmpObjekt := TToDo.create;
     Stream.ReadBuffer(TmpObjekt, sizeof(TmpObjekt));
     s := TmpObjekt.Kategorie;
     showmessage(s);
//     ListBox_todo.items.add(TmpObjekt.Kategorie + '-->' + TmpObjekt.Anzeigetext);
     end;
     TmpObjekt.free;
     Stream.Free;

 except on E: Exception do
 end;
 end;
end;


Delete - Sa 23.10.04 19:20

Ich glaube, dass ich das schon vorgestern in einem post geschrieben habe, aber gerne nochmal:
Objekte werden mit Zeigern verwaltet. Was du machst, ist lediglich, die 4-byte großen zeiger in deine Datei zu pumpen, was du natürlich vergessen kannst!

ich weiß nicht genau, wie dein Objekt da aufgebaut ist, deswegen kann ich dir auch nicht genau sagen, wie man es genau speichert. aber du solltest folgende methoden deinem objekt hinzufügen:


Delphi-Quelltext
1:
2:
 TToDo.LoadFromStream(stream: TStream);
 TToDo.SaveToStream(stream: TStream);


Wenn du deine Objektdeklaration mal herzeigst, wäre auch die Implementierung kein Problem. So gehts aber nicht!


schlumsch - Sa 23.10.04 19:47

...ajo das hast du auch schonmal geschrieben, nur hab ich mein "schlaues Büchlein" befragt und ein Beispiel gesehen wie die normale Records saven - eben ohne die von die erwähnten Methoden. Aber ich bin da flegibel *g*

Folgendermassen sieht mein Obj. aus...


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
type TToDo = class
private
public
     Kategorie : string[20];
     Anzeigetext : string[100];
     Text : string;
     Erzeugungsdatum : TDateTime;
     Constructor create;

end;


AndyB - So 24.10.04 00:52

Normalerweise bringt man den Objekten bei sich selbst in einen Stream zu speichern/laden. Wie Elite schon schrieb benutzt man dafür die LoadFromStream und SaveToStream Methoden(namen).
Alternativ erbst du von TPersistent und stellst alle Felder, die gespeichert werden sollen als published Eigenschaft bereit und verwendest zum Schreiben TWriter und zum Lesen TReader.

Es gibt zwar noch eine andere Lösung, die ist aber genauso "dumm" wie das Speichern der Referenzen (=Zeiger).


Delete - So 24.10.04 08:06

schlumsch hat folgendes geschrieben:
...ajo das hast du auch schonmal geschrieben, nur hab ich mein "schlaues Büchlein" befragt und ein Beispiel gesehen wie die normale Records saven - eben ohne die von die erwähnten Methoden.

Ja eben da liegt ja der Hase im Pfeffer. Records werden im Gegensatz zu Objekten nicht mit Zeigern verwaltet (im Endeffekt zwar schon, aber für den Programmierer erst mal nicht). Deswegen kannst du Records wie normale Variablen speichern.

schlumsch hat folgendes geschrieben:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
type TToDo = class
private
public
     Kategorie : string[20];
     Anzeigetext : string[100];
     Text : string;
     Erzeugungsdatum : TDateTime;
     Constructor create;

end;

Warum benutzt du überhaupt ein Objekt, wenn das eh nur Felder beinhaltet? Da täte ein Record seine Sache besser. :gruebel:
Außerdem kannste dir den Constructor sparen, der tut bei dir ja offentsichtlich eh nichts besonderes(oder?).

So solltest du deine Klasse erweitern:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
type TToDo = class
public
     Kategorie : string[20];
     Anzeigetext : string[100];
     Text : string;
     Erzeugungsdatum : TDateTime;
     Constructor create;
     procedure LoadFromStream(str: TStream);
     procedure SaveToStream(str: TStream);
end;
// hier die Implementierung...
procedure TToDo.LoadFromStream(str: TStream);
begin
 str.write(Kategorie, SizeOf(Kategorie));
 str.write(AnzeigeText, SizeOf(AnzeigeText));
 ...
// beim Laden halt mit str.load()


So, jetzt kannst du dich noch drum kümmern,
1. dynamische Strings zu speichern
2. TDateTime zu speichern
3. die Notwendigkeit einer Klasse abzuwägen


schlumsch - So 24.10.04 08:56

JO,

also ich dank dir erstmal - werds nachher gleich m,al versuchen.
Ich benutsz Objekte weil ichs einfach endlich mal angehen wollte.
Sicher wäre hier ein Record sinnvoller, seh ich schon ein, aber ich
wollte es zuerst aber an einem einfachen Beispiel ausprobieren, habe da noch `ne umfangreichere "Applikation" in Planung wo Objekte unumgänglich sind.

... na jedenfalls dank icvh dir wie wild...

thx schlumsch