Autor Beitrag
tuge
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Do 01.08.02 23:17 
Hallo Leute,

habe folgende Datenstruktur, die ich in einer Datei ablegen möchte:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
ZMaterial=^TMaterial;
TMaterial=record
        Name:string;
        Lager: boolean;
        Anzahl: integer;
        Preis: double;
        Lieferdauer: integer;
        id:integer;
        next:ZMaterial;
        end;

ZArbeiter=^TArbeiter;
TArbeiter=record
        Name:String;
        next:ZArbeiter;
        end;

THauptdaten=record
        Budget: double;
        Zeitauflage: integer;
        Material:ZMaterial;
        Arbeiter:ZArbeiter;
        end;


Wie Ihr an dieser Struktur erknnen könnt, fasst der Typ THauptdaten die Zeiger Material und Arbeiter. Nun möchte ich alle Daten aus THauptdaten in eine Datei schreiben ( mit allen Daten aus Material und Arbeiter).

Kann mir da einer helfen?

Vielen Dank!

Tuge
MrSpock
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 262



BeitragVerfasst: Fr 02.08.02 08:00 
Hallo tuge,

da könnte ich folgende Lösungen anbieten, für den Fall, dass wirklich alles in einer Datei stehen soll:

Du kannst das Ganze z.B. als CSV (Comma Separated Values) in eine ASCII Datei schreiben ohne die "Zeigerwerte". Dabei könntest du z.B. Satzkennzeichen verwenden, die immer als erstes in die Datei geschrieben werden. Z.B. Material Kennzeichen M, Arbeiter A und Hauptdaten H, dann könnte deine Datei wie folgt aussehen:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
H;200.34;3;
M;Schraube;F;3;2.8;23;001;
M;Mutter;F;3;1.8;21;002;
A;MrSpock;
A;CaptKirk;
A;Pille;


Beim Einlesen erkennst du am Tag, welcher Datentyp anzulegen ist und kannst die Struktur wieder erzeugen;

Alternativ dazu könntest du 3 Dateien anlegen und die Satzposition innerhalb der jeweiligen Datei als "Zeiger" verwenden. Schaue dir dazu mal die Hilfe zum Thema Seek und FilePos an. Diese Lösung ist eleganter und schneller, insbesondere wenn du auch wahlfrei auf einzelne Sätze zugreifen willst.

Letzte Lösung wäre die Verwendung einer Datenbank (z.B. Paradox) wobei du für Arbeiter und Material IDs anlegst. Da es wohl n:m Verknüpfungen gibt wird die Relation zwischen Hauptdaten und Arbeiters, bzw. Hauptdaten und Material als eigene Tabelle gespeichert. Wenn du dich für diese Lösung entscheidest und noch Fragen hast, sach einfach "BESCHEID" :D

_________________
Live long and prosper
MrSpock \\//
tuge Threadstarter
Hält's aus hier
Beiträge: 2



BeitragVerfasst: Fr 02.08.02 11:51 
Hey MrSpock,

über die Schlagworte "seek" und "FilePos" werde ich mir den Kram mal raussuchen. DBs kommen für mich hier nicht in Frage, da es sich bei diesem Programm um einen portablen Prototypen handelt und die Verwendung von Datenbanken diesen Rahmen und die portabilität (nun sind keine InstallShields erforderlich) doch sprengen würde.

Vielen Dank für Deine rasche Hilfe.

Tuge
Klabautermann
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Veteran
Beiträge: 6366
Erhaltene Danke: 60

Windows 7, Ubuntu
Delphi 7 Prof.
BeitragVerfasst: Fr 02.08.02 14:23 
Hallo tuge,

Da gibt es mehrere Möglichkeiten:

1. du erstellse einen Variablen Record (Online-Hilfe: Variante Teile in Record-Typen) und erstellst eine Datei von diesen (FILE OF).
Vorteil: Einfach zu ralisieren.
Nachteil:
-Du musst deine Records so modifizieren, dass sie eine Feste größe Haben - du darfs als keinen String-Datentyp ohne Größenbegrenzung verwenden.
- Du wirst viel verschnitt haben, da bei Jedem Datensatz die Maximale größe verendet wird, auch wenn der Aktuelle Typ diese nciht ausfüllt.
- Sehr problemanfällig bei änderungen (updates) deiner Datentypen.

2. Arbeite mit Untypisierten Dateien und überlege dir einen Datensatzheader den du mit in die Datei speicherst. Hierzu kannst du Untypisierte Dateien (FILE) oder tFileStream verwenden. Eisenherz hat sogear eine Freie Klasse mit tFileStream realisiert ich habe diese aber noch nciht gesichtet und weiß nicht wie gut sie auf dein Problem Anwendbar ist, aber vieleicht hift das Tutorial dazu.
Vorteile:
- Schnell da Untypisierter Dateizugriff.
- Du musst nur Nutz und Verwaltungsdaten Speichern, hast also keinen
- Wenn du dich geschickt bein entwickeln den Dateiformates anstellst kannst du das ganze Sehr Flexibel machen und so auch z.B. Versionskonflickten aus dem Weg gehen (bzw. jetzt schon berücksichtigen, das du Sie Später haben könntest).
Verschnitt.
Nachteil:
- Relativ aufwendig.

3. Arbeite mit Flexiblen Textdateien.
Solange du keine Binärdaten verwendest (Bilder o.ä.) Kannst du auf diese Variante zurückgreifen. Sie hat den Vorteil, du in eine Textdatei reinschreiben kannst was du Willst, Du musst nur erkennen können was wo steht. Zwei gute beispiele hierfür (die du beide verwenden kannst) sind INI-Files oder XML-Dateien.
Vorteile:
- Einfaches sehr Robustes Format.
- Kann notfalls auch von Hand (in einem Editor) gepflegt werden.
- Im Fall von INI-Files sehr einfacher Zugriff aus DELPHI (tIniFile).
Nachteile:
- Es Kann "Verbotene" Zeichen geben (z.B. '[' in den Rubrikbezeichnungen bei INI Files).
- Kann durch Elemente (Tags) größer werden (z.B. bei XML).
- Etwas langsamer.

Gruß
Klabautermann
MrSpock
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 262



BeitragVerfasst: Fr 02.08.02 14:34 
Hallo Klabautermann,

vielleicht zur Ergänzung:

Bei der Variante mit den varianten Records, muss auch die Adresse auf die nächsten Records (Material und Arbeiter) durch Ihre Position in der Datei ersetzt werden, denn das Abspeichern der echten Pointer funktioniert ja nicht.

_________________
Live long and prosper
MrSpock \\//
Klabautermann
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Veteran
Beiträge: 6366
Erhaltene Danke: 60

Windows 7, Ubuntu
Delphi 7 Prof.
BeitragVerfasst: Fr 02.08.02 15:08 
MrSpock hat folgendes geschrieben:
Bei der Variante mit den varianten Records, muss auch die Adresse auf die nächsten Records (Material und Arbeiter) durch Ihre Position in der Datei ersetzt werden, denn das Abspeichern der echten Pointer funktioniert ja nicht.


Stimmt, aber das Problem hast du eigentlich bei jeder Methode. Da du zwar Pointer speichern, aber nicht sicherstellen kannst das die verlinkten Daten auch nach dem Laden noch an der Stelle im Speicher sind.
Du kannst das Ganze über IDs oder die Dateistrucktur lösen. Ersteres ist währscheinlich unanfälliger gegen Fehler (durch änderungen an der Datei).

Gruß
Klabautermann
MrSpock
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 262



BeitragVerfasst: Fr 02.08.02 22:09 
Hallo Klabautermann,

ja, IDs sind sicher gut. Wenn du die Position des Datensatzes in der Datei benutzt, hast du aber wahlfreien Zugriff. Das ist auch ein Vorteil. :roll:

_________________
Live long and prosper
MrSpock \\//
Eisenherz
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 48



BeitragVerfasst: Fr 02.08.02 22:59 
Klabautermann hat folgendes geschrieben:
Eisenherz hat sogear eine Freie Klasse mit tFileStream realisiert ich habe diese aber noch nciht gesichtet und weiß nicht wie gut sie auf dein Problem Anwendbar ist, aber vieleicht hift das Tutorial dazu.


Finde ich gut, dass jetzt schon andere Benutzer Links auf meine Seiten setzen. :)

Meine Klassen sind vor allem dann interessant, wenn man damit rechnen muss, dass bei zukünftigen Programmversionen sich das Datenformat ändert, sprich erweitert wird.

Ich habe mal ein Beispiel gemacht, wie man die besagten Daten mit meinen Klassen speichern könnte.
Da ich kein Fan von Zeigern bin, habe ich keine verketteten Listen genommen, sondern TObjectList. Außerdem habe ich die records zu Klassen gemacht. Ich habe die Daten public gelassen, damit es hier übersichtlicher bleibt.

ausblenden volle Höhe 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:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224:
225:
226:
227:
228:
229:
230:
231:
232:
233:
234:
235:
236:
237:
238:
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
unit Mainform;

interface

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

type
  TMaterial = class
  public
    Name: string;
    Lager: boolean;
    Anzahl: integer;
    Preis: double;
    Lieferdauer: integer;
    id: integer;

    procedure SaveToBinaryTag(Tag: TRakBinaryTag);
    procedure LoadFromBinaryTag(Tag: TRakBinaryTag);
  end;

  TArbeiter = class
  public
    Name: string;

    procedure SaveToBinaryTag(Tag: TRakBinaryTag);
    procedure LoadFromBinaryTag(Tag: TRakBinaryTag);
  end;

  THauptdaten = class
  public
    Budget: double;
    Zeitauflage: integer;
    AlleArbeiter: TObjectList;
    AlleMaterialien: TObjectList;

    constructor Create;
    destructor Destroy; override;

    procedure SaveToBinaryTag(Tag: TRakBinaryTag);
    procedure LoadFromBinaryTag(Tag: TRakBinaryTag);
  end;

  TFrmMain = class(TForm)
    BtnLoad: TButton;
    BtnSave: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure BtnSaveClick(Sender: TObject);
    procedure BtnLoadClick(Sender: TObject);
  private
    { Private-Deklarationen }
    FHauptdaten: THauptdaten;
  public
    { Public-Deklarationen }
  end;

var
  FrmMain: TFrmMain;

implementation

{$R *.DFM}

{ TMaterial }

procedure TMaterial.LoadFromBinaryTag(Tag: TRakBinaryTag);
begin
  Name := Tag.AttributeAsString('Name', '');
  Lager := Tag.AttributeAsBoolean('Lager', false);
  Anzahl := Tag.AttributeAsInteger('Anzahl', 0);
  Preis := Tag.AttributeAsFloat('Preis', 0);
  Lieferdauer := Tag.AttributeAsInteger('Lieferdauer', 0);
  id := Tag.AttributeAsInteger('id', 0);
end;

procedure TMaterial.SaveToBinaryTag(Tag: TRakBinaryTag);
begin
  TRakBinaryAnsiString.AddToTag(Tag, 'Name', Name);
  TRakBinaryBoolean.AddToTag(Tag, 'Lager', Lager);
  TRakBinaryLongint.AddToTag(Tag, 'Anzahl', Anzahl);
  TRakBinaryDouble.AddToTag(Tag, 'Preis', Preis);
  TRakBinaryLongint.AddToTag(Tag, 'Lieferdauer', Lieferdauer);
  TRakBinaryLongint.AddToTag(Tag, 'id', id);
end;

{ TArbeiter }

procedure TArbeiter.LoadFromBinaryTag(Tag: TRakBinaryTag);
begin
  Name := Tag.AttributeAsString('Name', '');
end;

procedure TArbeiter.SaveToBinaryTag(Tag: TRakBinaryTag);
begin
  TRakBinaryAnsiString.AddToTag(Tag, 'Name', Name);
end;

{ THauptdaten }

constructor THauptdaten.Create;
begin
  AlleArbeiter := TObjectList.Create;
  AlleMaterialien := TObjectList.Create;
end;

destructor THauptdaten.Destroy;
begin
  AlleArbeiter.Free;
  AlleMaterialien.Free;

  inherited;
end;

procedure THauptdaten.LoadFromBinaryTag(Tag: TRakBinaryTag);
var
  i: integer;
  SubTags: IRakBinaryTagList;
  SubTag: TRakBinaryTag;
  Material: TMaterial;
  Arbeiter: TArbeiter;
begin
  Budget := Tag.AttributeAsFloat('Budget', 0);
  Zeitauflage := Tag.AttributeAsInteger('Budget', 0);

  AlleMaterialien.Clear;
  SubTags := Tag.FindTagsByName('Material');
  for i := 0 to SubTags.Count - 1 do
  begin
    Material := TMaterial.Create;
    AlleMaterialien.Add(Material);
    SubTag := SubTags.Items(i);
    Material.LoadFromBinaryTag(SubTag);
  end;

  AlleArbeiter.Clear;
  SubTags := Tag.FindTagsByName('Arbeiter');
  for i := 0 to SubTags.Count - 1 do
  begin
    Arbeiter := TArbeiter.Create;
    AlleArbeiter.Add(Arbeiter);
    SubTag := SubTags.Items(i);
    Arbeiter.LoadFromBinaryTag(SubTag);
  end;
end;

procedure THauptdaten.SaveToBinaryTag(Tag: TRakBinaryTag);
var
  i: integer;
  SubTag: TRakBinaryTag;
  Material: TMaterial;
begin
  TRakBinaryDouble.AddToTag(Tag, 'Budget', Budget);
  TRakBinaryLongint.AddToTag(Tag, 'Zeitauflage', Zeitauflage);

  // Beim Speichern der Materialien steht hier der Code ausführlich,
  // damit man ihn besser verstehen kann.
  for i := 0 to AlleMaterialien.Count - 1 do
  begin
    // Für jedes Material wird ein eigener Unterknoten erzeugt
    SubTag := Tag.AddTag('Material');
    // Die Referenz auf das Objekt besorgen
    Material := TMaterial(AlleMaterialien[i]);
    // Das Material speicher seine Daten selbst in dem neuen Unterknoten ab
    Material.SaveToBinaryTag(SubTag);
  end;

  // Beim Speichern der Arbeiter steht hier der Code kompakt
  for i := 0 to AlleArbeiter.Count - 1 do
    TArbeiter(AlleArbeiter[i]).SaveToBinaryTag(Tag.AddTag('Arbeiter'));
end;

{ TFrmMain }

procedure TFrmMain.FormCreate(Sender: TObject);
var
  Arbeiter: TArbeiter;
  Material: TMaterial;
begin
  FHauptdaten := THauptdaten.Create;

  // Wir füllen hier FHauptdaten, damit wir etwas zum speichern haben
  FHauptdaten.Budget := 5000;
  FHauptdaten.Zeitauflage := 30;

  Arbeiter := TArbeiter.Create;
  FHauptdaten.AlleArbeiter.Add(Arbeiter);
  Arbeiter.Name := 'Huber';

  Arbeiter := TArbeiter.Create;
  FHauptdaten.AlleArbeiter.Add(Arbeiter);
  Arbeiter.Name := 'Müller';

  Arbeiter := TArbeiter.Create;
  FHauptdaten.AlleArbeiter.Add(Arbeiter);
  Arbeiter.Name := 'Meier';


  Material := TMaterial.Create;
  FHauptdaten.AlleMaterialien.Add(Material);
  Material.Name := 'Eisen';
  Material.Lager := true;
  Material.Anzahl := 7;
  Material.Preis := 16.30;
  Material.Lieferdauer := 5;
  Material.id := 1;

  Material := TMaterial.Create;
  FHauptdaten.AlleMaterialien.Add(Material);
  Material.Name := 'Stein';
  Material.Lager := false;
  Material.Anzahl := 0;
  Material.Preis := 70.66;
  Material.Lieferdauer := 3;
  Material.id := 2;
end;

procedure TFrmMain.FormDestroy(Sender: TObject);
begin
  FHauptdaten.Free;
end;

procedure TFrmMain.BtnSaveClick(Sender: TObject);
var
  Data: TRakBinaryStreamData;
begin
  Data := TRakBinaryStreamData.Create;
  try
    FHauptdaten.SaveToBinaryTag(Data);
    Data.SaveToFile('c:\Test.dat');
  finally
    Data.Free;
  end;
end;

procedure TFrmMain.BtnLoadClick(Sender: TObject);
var
  Data: TRakBinaryStreamData;
begin
  Data := TRakBinaryStreamData.Create;
  try
    if Data.LoadFromFile('c:\Test.dat') = blrOK then
      FHauptdaten.LoadFromBinaryTag(Data);
  finally
    Data.Free;
  end;
end;

end.


Ich vermute, dass ich das Ganze noch etwas anders designen würde, wenn ich genau wüsste, was das Programm eigentlich verwalten soll. Außerdem habe ich zwecks Übersichtlichkeit noch andere Kleinigkeiten weggelassen oder anders gemacht, als ich es in einem produktiven Code gemacht hätte.
Wer meine Klassen verwenden will, der sollte vorher erst einmal das Tutorial durcharbeiten. Dort wird noch viel tiefer darauf eingegangen, wie man mit verschiedenen Programmversionen umgehen kann.

_________________
aloa Eisenherz
Klabautermann
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Veteran
Beiträge: 6366
Erhaltene Danke: 60

Windows 7, Ubuntu
Delphi 7 Prof.
BeitragVerfasst: Fr 02.08.02 23:41 
Eisenherz hat folgendes geschrieben:
Finde ich gut, dass jetzt schon andere Benutzer Links auf meine Seiten setzen. :)


Wenn die chance besteht das sie von nutzen ist ;). Auch auf die Gefahr hin den armen tuge noch mehr zu verwirren.

Gruß
Klabautermann