Entwickler-Ecke
Dateizugriff - Speichern von verketteten Listen in einer Datei
tuge - Do 01.08.02 23:17
Titel: Speichern von verketteten Listen in einer Datei
Hallo Leute,
habe folgende Datenstruktur, die ich in einer Datei ablegen möchte:
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 - 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:
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
tuge - 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 - 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 [
http://www.kasparsoft.de/RakBinaryStreamData/index.htm] 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 [
http://www.kasparsoft.de/RakBinaryStreamData/Tutorial/index.html] 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 - 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.
Klabautermann - 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 - 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:
Eisenherz - Fr 02.08.02 22:59
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.
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.
Klabautermann - 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
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!