Entwickler-Ecke

Dateizugriff - Speichern eines Objektes/Komponente in einer Datei


Schatten^parker - Fr 14.02.03 22:07
Titel: Speichern eines Objektes/Komponente in einer Datei
Hi leute,
ich habe wie immer wenn ich mich an euch wende ein problem!

zum verständnis:
ich schreibe ein programm dass eine klasse, tmitglied mit den eigenschaften .vorname .nachname blablabla, in eine liste einträgt.
nun möchte ich die Klasse in einer datei speichern, *.dat oder so, um sie später wieder laden zu können.

nun weiss ich ja nicht wie delphi eine klasse mit eigenschaften in der datei speichert (wenns jemand weiss bitte erklären), noch weiss ich ob man jede klasse in einer seperaten zeile speichern muss noch wie man allgemein sowas in dateien speichert.

bitte keine antwort mit fachsimpelei, danke

(( und bitte keine hilfeverweise, danke ))


Delete - Fr 14.02.03 22:10

Ich denke du sprichst nicht von einer Klasse, sondern von einem Record. Und den kannst mit typisierten Dateien abspeichern.


Andreas L. - Sa 15.02.03 09:07

Oder mit INI-Dateien, deren du einfach eine andere Dateiendung gibst!


maximus - Sa 15.02.03 15:18

Hi, natürlich kannst du auch klassen-properties in dateien speichern! Sie muss dazu von TPersistent abgeleitet sein und die properties müssen 'published' sein! Dann kannst du mittels RTTI (RunTimeTypeInformation) die props speichern und laden, und zwar automatisch alle auf einmal.

Wenn du von TComponent ableitest, dann kannst du dies sogar mit jedem beliebigen Stream tun -> Stream.writeComponent(compo) ,...readComponent(compo)!
Is aber nicht so schön, weil du dann viel unnötigen quatsch in der klasse hasst.

viel spass!

PS: auf [url]http://www.joachimDeVries.de[/url] findest du eine gute RTTI unit.


Schatten^parker - Sa 15.02.03 15:42

Ich verstehe nicht ganz was ich machen soll, hier mein quelltext zum verständnis! danke




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:
unit Mhaupt;

interface

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

type
  TFHaupt = class(TForm)
    bt_dialog: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Button1: TButton;
    lb_mitglied: TListBox;
    procedure bt_dialogClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  fdialog: Tfdialog;
  mitglied: tmitglied;
  mitgliedliste: Tlist;
  listbox: tlistbox;
    { Private-Deklarationen }
  public
  function getvorname: string;
  function getnachname: string;
  function getgebdatum: string;
  function getnummer: string;

    { Public-Deklarationen }
  end;

var
  FHaupt: TFHaupt;


implementation

{$R *.DFM}
function TFHaupt.getvorname;
begin
     getvorname:= fdialog.edit1.text;
end;

function TFHaupt.getnachname;
begin
     getnachname:= fdialog.edit2.text;
end;

function TFHaupt.getgebdatum;
begin
     getgebdatum:= fdialog.edit3.text;
end;

function TFHaupt.getnummer;
begin
     getnummer:= fdialog.edit4.text;
end;


procedure TFHaupt.bt_dialogClick(Sender: TObject);
begin
     fdialog:= tfdialog.create(SELF);
     fdialog.showmodal;

     edit1.text:= getvorname;
     edit2.text:= getnachname;
     edit3.text:= getgebdatum;
     edit4.text:= getnummer;

     mitglied:= tmitglied.create;
     mitglied.vorname:= getvorname;
     mitglied.nachname:= getnachname;
     mitglied.gebdatum:= getgebdatum;
     mitglied.nummer:=getnummer;
     mitgliedliste.add(mitglied);

    lb_mitglied.items.add(mitglied.nummer+' - '+mitglied.nachname+', '+mitglied.vorname+'('+mitglied.gebdatum+')');
   { mitgliedliste.items[0].savetofile('c:\schule\info\ml.dat'); }
    showmessage(mitglied.vorname+' wurde erfolgreich in die Liste aufgenommen!');

    fdialog.free;

end;

procedure TFHaupt.Button1Click(Sender: TObject);
begin
     close;
end;

procedure TFHaupt.FormCreate(Sender: TObject);
begin
     mitgliedliste:= tlist.create;
     listbox:= tlistbox.create(self);
    { mitgliedliste.items.loadfromfile('Mitgliedliste.dat');  }
end;

end.


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
unit mmitglied;

interface

type
  tmitglied = class(Tobject)
    vorname: string;
    nachname: string;
    gebdatum: string;
    nummer: string;
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;


implementation

end.


aogwaba - Sa 15.02.03 17:43

benutz doch statt TList( welches eigentlich nur Pointer aufnimmt), ein TStringList, da sind die Read und Write Methoden mit onBoard.

cu
waba


Schatten^parker - Sa 15.02.03 18:10


Quelltext
1:
[Fehler] Mhaupt.pas(81): Inkompatible Typen: 'String' und 'tmitglied'                    


die klasse mitglied kann nicht in die stringlist aufgenommen werden da sie warscheinlich garkein string ist, sie hatt properties vom typ string ( .vorname , .nachname ,...).


aogwaba - Sa 15.02.03 19:20

gemeint war das so:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
myList:tstringList;
myList:=tstringList.create;

mitglied:= tmitglied.create; 
mitglied.vorname:= getvorname; 
mitglied.nachname:= getnachname; 
mitglied.gebdatum:= getgebdatum; 
mitglied.nummer:=getnummer;
    
myList.add(mitglied.nummer+' - '+mitglied.nachname+', '
+mitglied.vorname+'('+mitglied.gebdatum+')');
   
myList.saveToFile(...);

Dieses Konstruct fügt die Strings zu einem String zusammen und speicht diesen.
Beim Laden muss dieser wieder zerbröselt, und die Token den einzelnen Propertys zugewiesen werden.

Sehr primitiv. Besser man benutzt, wie Luckie schon sagte, typisierte Dateien.
Hier ein Link: http://www.tutorials.delphi-source.de/sequdateien/

oder noch besser TFileStream.

cu
waba


maximus - Sa 15.02.03 20:30

Hallo! Hier die möglichkeit via RTTI...das rockt hart!

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:
unit mmitglied; 

interface 

type 
  tmitglied = class(TComponent) // TComponent weil es sich mit streams speichern lässt!
    Fvorname: string; 
    Fnachname: string; 
    Fgebdatum: string; 
    Fnummer: string; 
  private 
    { Private-Deklarationen } 
   published // edit
    { Public-Deklarationen }
    SaveToFile(FileName);
    LoadFromFile(FileName);

    property Vorname : string read FVorname write FVorname;
    property Nachname : string read FNachname  write FNachname ;
    .    
    .
    .
  end; 


implementation 

procedure tmitglied.SaveToFile; 
var fs:TFileStream;
begin
  fs:=TFileStream.create(fileName,fmCreate);
  try
    fs.writeComponent(self);
  finally
    fs.free;
  end;
end;

procedure tmitglied.LoadFromFile; 
var fs:TFileStream;
begin
  if not fileExists(filename) then exit;
  fs:=TFileStream.create(fileName,fmOpenRead);
  try
    fs.ReadComponent(self);
  finally
    fs.free;
  end;
end;

end.


Hierbei musst du dich halt nicht mehr um die einzelnen properties kümmern, da sie alle automatisch gespeichert werden (im binären DFM format). Mit 'ObjectBinaryToText' oder 'ObjectTextToBinary' könntest du das format sogar in regulären DFM-text convertieren und zurück!

IMHO sehr cool, da voll dynamisch :wink:

mfg maximus


Schatten^parker - So 16.02.03 16:36

Hi Maximus, danke für deinen Tip.
Etwas ähnliches habe ich auch schon probiert, aber jedes mal wenn ich eine Klasse von TComponent ableiten will kommt folgender Fehler


Quelltext
1:
[Fehler] mmitglied.pas(6): Undefinierter Bezeichner: 'TComponent'                    


Ich weiss auch nicht woran es liegt, vielleicht hab ich vergessen was zu deklrieren??? bitte hilf mir ;-)))


Tino - So 16.02.03 17:28

Du musst die Datei Classes einbinden:

Quelltext
1:
2:
Uses
  Classes;


Gruß
TINO


maximus - So 16.02.03 18:26

Genau! Mit 'ObjectBinaryToText' oder 'ObjectTextToBinary' echten DFM-text zu erzeugen ist nicht ganz so leicht! Deshalb der vollständigkeit halber, wie ich es gemacht habe:

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:
...
function SaveCompoToFile(aCompo : TComponent; FileName: string; asBinary: Boolean = false): boolean;
var Stream1:TMemoryStream;
    Stream2:TMemoryStream;
    format : TStreamOriginalFormat;
begin
  result := false;
  format  := sofText;
  Stream1 := TMemoryStream.Create;
  Stream2 := TMemoryStream.Create;
  try
    Stream1.WriteComponent(aCompo);
    Stream1.Position := 0;
    if not asBinary then
    begin
      format := sofText;
      ObjectBinaryToText(Stream1,Stream2,format);
    end else Stream1.SaveToStream(Stream2);
    Stream2.SaveToFile(FileName);
    result := true;
  finally
    Stream1.Free;
    Stream2.Free;
  end;
end;


function LoadCompoFromFile(aCompo : TComponent; FileName: string; asBinary: Boolean = false): boolean;
var FileStream:TFileStream;
    MemoryStream:TMemoryStream;
    format:TStreamOriginalFormat;
begin
  result := false;
  if not FileExists(fileName) then exit;
  MemoryStream := TMemoryStream.Create;
  FileStream := TFileStream.Create(FileName,fmOpenRead);
  try
    if not asBinary then
    begin
      format := sofBinary;
      ObjectTextToBinary(FileStream,MemoryStream,format);
    end else MemoryStream.LoadFromStream(FileStream);
    MemoryStream.Position := 0;
    MemoryStream.ReadComponent(aCompo);
    result := true;
  finally
    FileStream.Free;
    MemoryStream.Free;
  end;
end;
...wer an einer lösung interessiert, die ohne TComponent auskommt und mit der man auch einzelne properties speichern kann, der soll sich hier melden :D

mfg maximus


Eisenherz - Mo 17.02.03 18:59

maximus hat folgendes geschrieben:
Hierbei musst du dich halt nicht mehr um die einzelnen properties kümmern, da sie alle automatisch gespeichert werden (im binären DFM format).


Somit kann man mit sehr wenig Code etwas speichern. Ich habe aber das Gefühl, dass das unter die Rubrik "Quick and dirty" fällt.
Das automatische Speichern klappt nur, wenn das Property die Sichtbarkeit Published hat. Will man etwas Speichern, was vom Konzept her Private sein sollte, dann wird man gezwungen dies Published zu machen. Das stimmt nicht ganz, da es auch eine Möglichkeit gibt, dass man "Properties" speichern kann, die nicht published sind. Dann muss man allerdings deutlich mehr Code schreiben.
Weiterhin ist man so gezwungen ein published Property bis in alle Ewigkeit beizubehalten. Man kassiert eine Exception, wenn man versucht eine Komponente aus einer Datei zu laden, die erzeugt wurde, als die Komponente noch Properties hatte, über die sie jetzt nicht mehr verfügt.


Tino - Di 18.02.03 10:12

Hallo Leute,

eine weitere Möglichkeit wäre mal auf die Website [http://www.kasparsoft.de/RakBinaryStreamData/index.htm] von Eisenherz zu gehen. Dort kann man sich die Unit RakBinaryStreamData downloaden. Dadurch wird das speichern und laden von beliebigen Daten vereinfacht.

Gruß
TINO


FBrk - Di 18.02.03 11:51
Titel: tud dat not???
Sag mal muss das sein das die Daten in einer Klasse gespeichert werden???
Klassen sind da um Daten und Routinen in einer Komponente zu verbinden und zu vererben um dann neue Komponenten hinzu zu fürgen. In diesem Falle ist ein Record das mittel der wahl, und das Speichert man logischer weise in Typisierten Datein.
Das ist erstens einfacher und zweitens sauberer.

Gruß Felix


maximus - Di 18.02.03 14:27

Eisenherz hat folgendes geschrieben:
Somit kann man mit sehr wenig Code etwas speichern. Ich habe aber das Gefühl, dass das unter die Rubrik "Quick and dirty" fällt.
Das automatische Speichern klappt nur, wenn das Property die Sichtbarkeit Published hat. Will man etwas Speichern, was vom Konzept her Private sein sollte, dann wird man gezwungen dies Published zu machen. Das stimmt nicht ganz, da es auch eine Möglichkeit gibt, dass man "Properties" speichern kann, die nicht published sind. Dann muss man allerdings deutlich mehr Code schreiben...


8) Klar is das Quick&Dirty (hab ich aber auch, in einem nebensatz draufhin gewiesen) , deshalb hab ich ja auch eine möglichkeit angeboten das quick aber nicht dirty zu machen:
Zitat:
...wer an einer lösung interessiert, die ohne TComponent auskommt und mit der man auch einzelne properties speichern kann, der soll sich hier melden.
. ...mit einem einzigen befehl zB. Width, height,top,left,windowState zu speichern ist IMHO sehr geil. Bei allen componenten bezogenen sachen hat man die wichtigen props eh immer published( zumindest die, die gespeichert werden sollen). Und schicken DFM-text zu haben ist halt auch nicht zu verübeln, da man die datei dann automatisch als config benutzen kann, was man bei einem binär-stream getrost vergessen kann :D

Mich würd mal interessieren wie ich nicht 'published' properties automatisch speicherst...wie machst du das, via RTTI oder hab ich was verpasst?

Grüsse.
maximus

PS: Der code oben eignet sich prima um ganze Componenten-bäume zur laufzeit zu erstellen und deren aktuelle einstellungen zu speichern! Die von delphi erstellten compos sollte man damit nicht speichern wollen, weil er dann versucht, die im DFM-text definierten compos zu erstellen, obwolh sie schon existieren :?


Eisenherz - Di 18.02.03 15:44
Titel: Re: tud dat not???
FBrk hat folgendes geschrieben:
Sag mal muss das sein das die Daten in einer Klasse gespeichert werden???

Wenn man streng objektorientiert programmiert, dann ja.

Zitat:
Klassen sind da um Daten und Routinen in einer Komponente zu verbinden und zu vererben um dann neue Komponenten hinzu zu fürgen.

Klassen wissen erst einmal gar nichts von Komponenten. Komponenten zeichnen sich dadurch aus, dass sie von der Klasse TComponent direkt oder indirekt abgeleitet sind. Eine Komponente ist also nur ein Spezialfall einer Klasse.
Zitat:
In diesem Falle ist ein Record das mittel der wahl, und das Speichert man logischer weise in Typisierten Datein.

Man kann Records in typisierten Dateien speichern, das ist aber nicht die einzige Möglichkeit und somit nicht logisch.
Records sind ein Überbleibsel aus der nicht objektorientierten Zeit von Pascal. Bei Java gibt es z.B. keine Records. Das braucht man auch nicht, da Records nur ein Spezialfall von Klassen sind, nämlich Klassen ohne Methoden.
Der einzig wirklich gewichtige Grund, der mir jetzt einfällt, um Daten in typisierten Dateien zu speichern ist, dass man schnellen Zugriff auch auf Daten braucht, die sich in der Mitte oder am Ende der Datei befinden.
Wenn man bedenkt, dass heutige Festplatten 20MByte/sec und mehr übertragen, lohnt sich der Einsatz von typisierten Dateien nur dann richtig, wenn man große Datenmengen hat. Dann sollte man aber darüber nachdenken, ob man nicht gleich mit einer richtigen Datenbank arbeitet.

Zitat:
Das ist erstens einfacher

Bei kleinen Programmen mag das stimmen.

Zitat:
und zweitens sauberer.

Das kommt darauf an, was man als "sauber" ansieht.


Eisenherz - Di 18.02.03 16:17

maximus hat folgendes geschrieben:
8) Klar is das Quick&Dirty (hab ich aber auch, in einem nebensatz draufhin gewiesen) , deshalb hab ich ja auch eine möglichkeit angeboten das quick aber nicht dirty zu machen:
Zitat:
...wer an einer lösung interessiert, die ohne TComponent auskommt und mit der man auch einzelne properties speichern kann, der soll sich hier melden.
.


Mich würde Deine Lösung interessieren. Ich glaube zwar nicht, dass ich sie einsetzen werde, da ich viel von Datenkapselung halte und mir somit der Gedanke nicht gefällt, dass ich Properties published und schreibbar machen muss, die vom Konzept her protected/private oder readonly sind. Aber vielleicht hast Du ein paar interessante Ideen gehabt.

Zitat:
Und schicken DFM-text zu haben ist halt auch nicht zu verübeln, da man die datei dann automatisch als config benutzen kann, was man bei einem binär-stream getrost vergessen kann :D

Textdateien haben natürlich den Vorteil, dass man sie sogar mit Notepad bearbeiten kann. Sie haben damit aber auch den Nachteil, dass man sie schnell unbrauchbar machen kann.
Damit man meinem "binär-stream" editieren kann habe ich extra ein Tool erstellt.

Zitat:
Mich würd mal interessieren wie ich nicht 'published' properties automatisch speicherst...wie machst du das, via RTTI oder hab ich was verpasst?

Ich speichere bisher nichts automatisch. Aber vielleicht greife ich den Gedanken auf und erweitere meine Klassen in diese Richtung.


maximus - Di 18.02.03 18:13

@Eisenherz:
Zitat:
Mich würde Deine Lösung interessieren. Ich glaube zwar nicht, dass ich sie einsetzen werde, da ich viel von Datenkapselung halte und mir somit der Gedanke nicht gefällt, dass ich Properties published und schreibbar machen muss, die vom Konzept her protected/private oder readonly sind. Aber vielleicht hast Du ein paar interessante Ideen gehabt.

Dich zwingt ja auch niemand irgendwas gegen deinen willen schreibbar zumachen :D Wenn deine props vom konzept her privat sein sollen, dann ist es ja auch unwahrscheinlich das du sie auf platte speichern willst, oder?

Du hast mich auf die idee gebracht, meinen ansatz mal auf read-only properties zu prüfen...die funktion ist da aber ob ich es hier auch berücksichtigt habe werde ich mal schecken.

Ich poste morgen mal den code.

bis denn. mx


maximus - Mi 19.02.03 00:46

So. Ich hab meinen code auf 'read-only' und 'stored false' properties sensibilisiert. Damit der code läuft, wird noch eine (sehr gute, IMO) RTTI unit [http://www.joachimdevries.de/files/RTTIDemo.zip] benötigt (von Joachim De Vries).

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:
{-----------------------------------------------------------------------------
 Unit Name : mxService
 Author    : maximus (mäxeus, maxantat, muku)
 Date      : 10.02.2003
-----------------------------------------------------------------------------}
unit mxService;

interface

uses classes, sysUtils, TypInfo, dvRTTI {<-- von www.joachimDeVries.de -} ;

function SaveObjectToFile(aObject : TPersistent; FileName: string; includedProps : String = '*'; filter : TTypeKinds = tkAny): boolean;
function LoadObjectFromFile(aObject : TPersistent; FileName: string; includedProps : String = '*'; filter : TTypeKinds = tkAny): boolean;

procedure CleanSpacesVar(var s:String; inQuotes:boolean=false; QuoteChar: Char = '''');
function CleanSpaces(s:String; inQuotes:boolean=false; QuoteChar:Char = ''''):string;

implementation

function CleanSpaces(s:String; inQuotes:boolean=false; QuoteChar:Char = ''''):string;
var i:integer;
    f:boolean;
begin
  result := s;
  i := 0;
  f := false;
  while i <= length(result) do
  begin
    if not inQuotes and (result[i] = QuoteChar) then f := not f;
    if (result[i] = ' ') and not f then Delete(result,i,1) else inc(i);
  end;
end;

procedure CleanSpacesVar(var s:String; inQuotes:boolean=false; QuoteChar:Char = '''');
var i:integer;
    f:boolean;
begin
  i := 0;
  f := false;
  while i <= length(s) do
  begin
    if not inQuotes and (s[i] = QuoteChar) then f := not f;
    if (s[i] = ' ') and not f then Delete(s,i,1) else inc(i);
  end;
end;

function isWholeWordInString(aWord,s:string; delimiter:char = ','):boolean;
var n,i:integer;
    m:string;
    c1,c2:char;
begin
  result := false;
  m := delimiter+lowercase(s)+delimiter;
  n := pos(lowercase(aWord),m);
  if n <= 0 then exit;
  m := copy(m,n-1,length(m)-n+2);
  c1 := m[1];
  c2 := m[length(aWord)+2];
  if ((c1 = delimiter) or (c1 = ' '))and((c2 = delimiter) or (c2 = ' '))
  then result := true
  else begin
         delete(m,2,length(aWord));
         result := isWholeWordInString(aWord, m, delimiter);
       end;
end;


//----------------------------------------------------------------------------------------


function SaveObjectToFile(aObject : TPersistent; FileName: string; includedProps : String = '*'; filter : TTypeKinds = tkAny): boolean;
var RTTI:TdvRTTIList;
    sl:TSTringList;
    i:integer;
    s,v,name:string;
    All:boolean;
begin
  result := false;
  if aObject = nil then exit;
  All := includedProps = '*';
  RTTI := TdvRTTIList.create(aObject);
  sl := TSTringList.Create;
  try
    RTTI.Filter := filter;
    if aObject is TComponent then name := TComponent(aObject).Name+' : ' else name := '';
    sl.Add('object '+name+aObject.ClassName);
    for i := 0 to RTTI.count-1 do
    begin
      if RTTI.Items[i].PropInfo.StoredProc = nil then continue;
      if not All and not isWholeWordInString(RTTI.Items[i].Name,includedProps) then continue;
      v := RTTI.Items[i].Value;
      if v <> '' then sl.Add('  ' + RTTI.Items[i].Name+' = '+ v);
    end;
    sl.Add('end');
    sl.SaveToFile(fileName);
    result := true;
  finally
    sl.Free;
    RTTI.Free;
  end;
end;

function LoadObjectFromFile(aObject : TPersistent; FileName: string; includedProps : String = '*'; filter : TTypeKinds = tkAny): boolean;
var RTTI:TdvRTTIList;
    sl:TSTringList;
    i:integer;
    s,name:string;
    All : boolean;
begin
  result := false;
  if aObject = nil then exit;
  if not FileExists(fileName) then exit;
  All := includedProps = '*';
  RTTI := TdvRTTIList.create(aObject);
  sl := TSTringList.Create;
  try
    RTTI.Filter := filter;
    sl.LoadFromFile(fileName);
    sl.Text := CleanSpaces(sl.Text);
    for i := 0 to RTTI.count-1 do
    begin
      if RTTI.Items[i].PropInfo.SetProc = nil then continue;
      name := RTTI.Items[i].Name;
      if not All and not isWholeWordInString(name,includedProps) then continue;
      if sl.IndexOfName(name) > -1
      then RTTI[i].Value := sl.Values[name];
    end;
    result := true;
  finally
    sl.Free;
    RTTI.Free;
  end;
end;

end.

Bindet man die unit ein, so kann man nu beliebige 'published' props eines objekts speichern und laden (ab D6):

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
uses mxService;
...
SaveObjectToFile(self, MyDir+self.Name+'.dmx','top,left,width,height,name,windowstate');
LoadObjectFromFile(self, MyDir+self.Name+'.dmx','top,left,width,height,name,windowstate');
// oder
SaveObjectToFile(self, MyDir+self.Name+'.dmx','*');  // speichert alle
LoadObjectFromFile(self, MyDir+self.Name+'.dmx','*');  // speichert alle
// oder
uses mxService, typInfo;
...
SaveObjectToFile(self, MyDir+self.Name+'.dmx','*', tkInteger); // speichert alle integer props
LoadObjectFromFile(self, MyDir+self.Name+'.dmx','*', tkInteger);

Das objekt muss von TPersistent abgeleitet sein(kann man vielleicht auch umbauen für Objekte die mit {M+} erstellt worden sind, allerdings beide units :? besser nicht!)

Viel spass damit...und wenn jemand verbesserungen hat, dann würd es mich freuen diese zu sehen(hier oder an maximus@eyer-systems.de).

mfg mx.


Eisenherz - Mi 19.02.03 16:17

Der Code ist auf alle Fälle sehr kompakt.

maximus hat folgendes geschrieben:
So. Ich hab meinen code auf 'read-only' und 'stored false' properties sensibilisiert.

Ich vermute dass die Zeile
Zitat:

Quelltext
1:
if RTTI.Items[i].PropInfo.StoredProc = nil then continue;                    

das stored false abprüfen soll. Ich habe auf die Schnelle keine vernünftige Hilfe zu StoredProc gefunden (In der Hilfe ist es zwar erwähnt aber nicht weiter beschrieben) und kann es leider momentan auch nicht ausprobieren. Ich hätte aber vermutet, dass StoredProc ein Zeiger auf die Methode wäre, die ermittelt, ob das Property gespeichert werden soll.
Ich hätte es vermutlich mit der Funktion IsStoredProp aus der Unit TypInfo versucht.

Zitat:

Quelltext
1:
if v <> '' then sl.Add('  ' + RTTI.Items[i].Name+' = '+ v);                    

Wieso speicherst Du nur Properties, die als Wert nicht den Leerstring haben? Ich wüsste nicht, warum ein string-Property das als Wert den Leerstring hat nicht abgespeichert werden soll?


Was passiert, wenn man ein published Property vom Typ TBitmap oder irgendeiner anderen Klasse hat?

Das ursprüngliche Problem von Schatten^Parker war das Speichern von einer nicht festgelegten Anzahl von Mitgliedern in einer Datei. Kann man mit Deinen Funktionen dieses Problem lösen?


maximus - Mi 19.02.03 19:34

8) Dir kann ma auch garnichts recht machen. Zugeben man kann nicht alle sonderfälle damit speichern, aber dies war uhrsprünglich auch nur dafür gedacht mir das speichern, von konfigurationen, meine anwendung abzunehmen.
Zitat:
das stored false abprüfen soll. Ich habe auf die Schnelle keine vernünftige Hilfe zu StoredProc gefunden (In der Hilfe ist es zwar erwähnt aber nicht weiter beschrieben) und kann es leider momentan auch nicht ausprobieren. Ich hätte aber vermutet, dass StoredProc ein Zeiger auf die Methode wäre, die ermittelt, ob das Property gespeichert werden soll.
Ich hätte es vermutlich mit der Funktion IsStoredProp aus der Unit TypInfo versucht.
...ich habs halt so gemacht, weil ich glaube das in 'storedProc' nur die adresse der 'getProc' drinne ist, die somit bei NIL einfach übergangen wird...solange es funktioniert...und das tut es :D

Zitat:
Wieso speicherst Du nur Properties, die als Wert nicht den Leerstring haben? Ich wüsste nicht, warum ein string-Property das als Wert den Leerstring hat nicht abgespeichert werden soll?
...is mir auch entfallen...kann man getrost löschen!
Zitat:
Was passiert, wenn man ein published Property vom Typ TBitmap oder irgendeiner anderen Klasse hat?

Tja...verdammt, hiermit leider nicht! Deshalb hab ich ja auch nochwas ganz anderes programmiert :lol: zB. für meine DirectX engine hab ich klasse gemacht, die wie TComponent funktioniert(der ganze quatsch weg), damit kann ich gigantische object-bäume speicher, indem ich einen DFM-konformen stream erzeuge! Dann kann ich mit 'ObjectBinaryToText' und 'ObjectTextToBinary' den stream in DFM-text machen, der unter anderem auch TBitmap darstellen kann.

Die lösung weiter oben (mit TComponent), schluckt ohne weites hunderte objekte, auch vom typ TBitmap.
Zitat:
Das ursprüngliche Problem von Schatten^Parker war das Speichern von einer nicht festgelegten Anzahl von Mitgliedern in einer Datei. Kann man mit Deinen Funktionen dieses Problem lösen?

davon sind wir jetzt eh weit entfernt...abgesehen davon kann er (wenn er kann) diese lösung einfach modifizieren, sodass er mehrere objekte rein tut :twisted:

Worauf ich, mit alle dem, hinnaus wollte ist eigentlich nur, dass die leute nicht jeden blöden wert einzeln speichern müssen...und einfach mal ein paar dynamische wege aufzeigen! -> Ich bin halt ein fan der klasse TFiler.

Hast du denn bessere vorschläge? :D

mfg maximus.


Eisenherz - Do 20.02.03 19:05

maximus hat folgendes geschrieben:
Deshalb hab ich ja auch nochwas ganz anderes programmiert :lol: zB. für meine DirectX engine hab ich klasse gemacht, die wie TComponent funktioniert(der ganze quatsch weg), damit kann ich gigantische object-bäume speicher, indem ich einen DFM-konformen stream erzeuge! Dann kann ich mit 'ObjectBinaryToText' und 'ObjectTextToBinary' den stream in DFM-text machen, der unter anderem auch TBitmap darstellen kann.


Wenn das allgemein verwendbare Klassen sind, dann wäre es doch schön, wenn Du diese der Allgemeinheit als OpenSource zur Verfügung stellen würdest. Der ein oder andere könnte so etwas vielleicht auch gebrauchen.

Zitat:
Hast du denn bessere vorschläge? :D

Es ist meistens Geschmackssache, ob man etwas als besser oder schlechter empfindet. Ich bin einfach immer auf der Suche nach Ideen, die mir mein Programmiererleben leichter machen. :D


maximus - Fr 21.02.03 11:22

*g*
Zitat:
Wenn das allgemein verwendbare Klassen sind, dann wäre es doch schön, wenn Du diese der Allgemeinheit als OpenSource zur Verfügung stellen würdest. Der ein oder andere könnte so etwas vielleicht auch gebrauchen.
...Sind relativ allgemein...und brauchen kann man sie auch, IMHO sehr gut. Hatte sowieso geplannt die gesammte engine 'frei zu stellen' wenn sie fertig ist. Ich könnte allerdings, schon vorher den 'base-teil' veröffentlichen...muss allerdings vorher noch meine HP redesignen.
Zitat:
Es ist meistens Geschmackssache, ob man etwas als besser oder schlechter empfindet. Ich bin einfach immer auf der Suche nach Ideen, die mir mein Programmiererleben leichter machen.

:D 1. klar, über geschmack lässt sich.... :D 2. Ich suche auch immer neue möglichkeiten und vor allem auch generelle technicken. Ich würd mich freuen, wenn hier auch mehr erkenntnisse präsentiert würden, die der allgemeinheit dienen, und nicht immer nur einzelnen -> *Wie mach ich das und das bunt* und dann kommt einer und sagt *kuckst du hier* (übertreibung). -> Vielleicht sollte man mal eine neue Source-base aufmachen, wo alle ihren tollen code reinladen können und die anderen können ihn dann voten :wink:

mfg maximus