![]() |
Daten per Streams in Datei schreiben
Hallo Leute!
Gleich vorweg: Ich habe noch nicht oft mit Streams gearbeitet. Ich möchte per Stream verschiedene Daten in eine Datei schreiben und wieder lesen können. Im Detail sind die Daten Klassen, welche irgendwelche Informationen enthalten (Beschreibung, etc...). Die Klassen kennen sich untereinander nicht. Somit muss ich die Daten im Stream anhand von Headern identifizieren können. In meiner Vorstellung sieht das so aus: |_HeaderA_|_DataA___________|_HeaderB_|_DataB_____ ______|_HeaderC_|_DataC___________| Nur leider habe ich keine Idee, wie ich das umsetzen kann :( So sieht mein Testframe aus:
Delphi-Quellcode:
Es wäre nett, wenn mir jemand Hilfestellung geben könnte :stupid:
unit FmStreamOperations;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type (* This is the first record that should be saved to a file most important properties: @ version field that can be interpreted from reader classes @ conentlen field when the record ends and the next record begins NOTE: packed record because the memory alignment of regular records are subject to change between releases *) TDummyHeaderA = packed record // after structure changes increment this Version: Integer; // position of begin of content Pos: Integer; // Integer should be enough to prevent to low allocation for the content Len: LongInt; // do not use dynamic length; ShortString (String[255]) instead of String HeaderType: ShortString; end; (* This is the second record that should be saved to a file most important properties: @ version field that can be interpreted from reader classes @ conentlen field when the record ends and the next record begins NOTE: packed record because the memory alignment of regular records are subject to change between releases *) TDummyHeaderB = packed record // after structure changes increment this Version: Integer; // position of begin of content Pos: Integer; // Integer should be enough to prevent to low allocation for the content Len: Integer; // do not use dynamic length; ShortString (String[255]) instead of String HeaderType: ShortString; end; (* This is the third record that should be saved to a file most important properties: @ version field that can be interpreted from reader classes @ conentlen field when the record ends and the next record begins NOTE: packed record because the memory alignment of regular records are subject to change between releases *) TDummyHeaderC = packed record // after structure changes increment this Version: Integer; // position of begin of content Pos: Integer; // Integer should be enough to prevent to low allocation for the content Len: Integer; // do not use dynamic length; ShortString (String[255]) instead of String HeaderType: ShortString; end; (* This is a dummy class for testing the header TDatasetHeaderA *) TDummyA = class(TPersistent) private FValueA: Integer; FValueB: string; FValueC: Boolean; public constructor Create; destructor Destroy; override; property ValueA: Integer read FValueA write FValueA; property ValueB: string read FValueB write FValueB; property ValueC: Boolean read FValueC write FValueC; end; (* This is a dummy class for testing the header TDatasetHeaderB *) TDummyB = class(TPersistent) private FValueA: Int64; FValueB: ShortString; FValueC: PChar; public constructor Create; destructor Destroy; override; property ValueA: Int64 read FValueA write FValueA; property ValueB: ShortString read FValueB write FValueB; property ValueC: PChar read FValueC write FValueC; end; (* This is a dummy class for testing the header TDatasetHeaderC *) TDummyC = class(TPersistent) private FValueA: SmallInt; FValueB: Char; FValueC: Double; public constructor Create; destructor Destroy; override; property ValueA: SmallInt read FValueA write FValueA; property ValueB: Char read FValueB write FValueB; property ValueC: Double read FValueC write FValueC; end; (* Testframe form *) TFmTestframe = class(TForm) ButtonSave: TButton; ButtonLoad: TButton; procedure ButtonSaveClick(Sender: TObject); procedure ButtonLoadClick(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; (* constants *) const C_FILE_EXT = 'bin'; const C_FILE_LOC = 'C:\'; var FmTestframe: TFmTestframe; implementation {$R *.dfm} procedure TFmTestframe.ButtonLoadClick(Sender: TObject); var Stream: TFileStream; FileName: string; HeaderA: TDummyHeaderA; DataA: TDummyA; begin // Try to load TDummyHeaderA & TDummyA from file via stream to the objects FileName := Format('%s.ExampleFileWrittenWithStream.%s', [C_FILE_LOC, C_FILE_EXT]); // !! don't forget to check if the file exist and is accessable in production builds Stream := TFileStream.Create(FileName, fmOpenRead); try // Stream.Read(...) <----- ??? What to do here? finally FreeAndNil(Stream); end; end; procedure TFmTestframe.ButtonSaveClick(Sender: TObject); var Stream: TFileStream; HeaderA: TDummyHeaderA; DataA: TDummyA; FileName: string; begin // Try it with TDummyHeaderA & TDummyA first // Fill TDummyHeaderA with data HeaderA.Version := 1; HeaderA.Pos := 0; { replace with start point of the content??? } HeaderA.Len := 0; { replace with the length of the content??? } HeaderA.HeaderType := '[Type] Dummy Header A'; // TDummyA is filled with dummy data by the constructor DataA := TDummyA.Create; try // Try to save TDummyHeaderA & TDummyA into file via stream FileName := Format('%s.ExampleFileWrittenWithStream.%s', [C_FILE_LOC, C_FILE_EXT]); // !! don't forget to check if the file exist and is accessable in production builds Stream := TFileStream.Create(FileName, fmCreate); try // Stream.Write(HeaderA, ...) <----- ??? What to do here? // ... // .. // . finally FreeAndNil(Stream); end; finally FreeAndNil(DataA); end; end; { TDummyA } constructor TDummyA.Create; begin FValueA := 1; FValueB := 'DummyA Example Text'; FValueC := True; end; destructor TDummyA.Destroy; begin inherited; end; { TDummyB } constructor TDummyB.Create; begin FValueA := 9823323232313; FValueB := 'Dummy B'; FValueC := PChar('Pointer to string test'); end; destructor TDummyB.Destroy; begin inherited; end; { TDummyC } constructor TDummyC.Create; begin FValueA := -1; FValueB := 'C'; FValueC := 3.14; end; destructor TDummyC.Destroy; begin inherited; end; end. |
AW: Daten per Streams in Datei schreiben
Schau die mal TWriter an. Sollte genau das tun was du willst.
Beispiel:
Delphi-Quellcode:
procedure TIrgendwas.SaveToStream(AStream : TStream); var Writer : TWriter; begin Writer := TWriter.Create(AStream,$10000000); try Writer.WriteString('<BEGINVERSION>'); Writer.WriteInteger(majorVer); Writer.WriteInteger(minorVer); Writer.WriteString('<ENDVERSION>'); Writer.FlushBuffer; finally Writer.Free; end; end; |
AW: Daten per Streams in Datei schreiben
Geht es nun konkret um eine bestimmte Architektur oder eher darum, wie du nun mit der TFileStream Klasse arbeitest?!
|
AW: Daten per Streams in Datei schreiben
Zitat:
Zitat:
|
AW: Daten per Streams in Datei schreiben
Der Vollständigkeit halber:
Mit TReader liest du das ganze wieder ein. |
AW: Daten per Streams in Datei schreiben
Hallo,
warum hast Du die Header (a..c) dreimal als Typ deklariert? Die Typen unterscheiden, so weit ich das gesehen habe, nicht. Grüße Klaus |
AW: Daten per Streams in Datei schreiben
Zitat:
Die tatsächlichen Typen werden sich unterscheiden. Ich habe das es nun mit dem TWriter/TReader probiert und siehe da...es funktioniert! Nun stellt sich mir die Frage, wie ich z. B. nur den Header C und dessen Content auslesen kann? |
AW: Daten per Streams in Datei schreiben
Zitat:
|
AW: Daten per Streams in Datei schreiben
Insgeheimer Bankeinzug bei Instanzierung..
|
AW: Daten per Streams in Datei schreiben
Zitat:
Die Position wäre dann: 0+sizeOf(HeaderA)+sizeOf(dataA)+sizeOf(HeaderB)+si zeOf(dataB). Header.len gibt das die Größe des Datenpaketes an? Wenn ja, dann kannst Du auch HeaderX.len anstelle von sizeOf(dataX) benutzen. Grüße Klaus |
AW: Daten per Streams in Datei schreiben
Wenn du mit "Tags" arbeitest kann es dir sogar egal sein ob Typ A und/oder B überhaupt vorhanden sind
Delphi-Quellcode:
with Reader do begin
while NextValue <> vaString do SkipValue; RLine := ReadString; if RLine='<BEGINOFHEADERTYPC>' then begin LokalerRecordTypC.Version := ReadInteger; LokalerRecordTypC.Pos := ReadInteger; LokalerRecordTypC.Len := ReadInteger; LokalerRecordTypC.HeaderType := ReadString; // hier bin ich mir nicht ganz sicher; ausprobieren end; end; |
AW: Daten per Streams in Datei schreiben
Wenn du auch noch Blöcke verwendest, dann können die Werte für BEGINOFHEADERTYPC zusammengefasst werden und es lässt sich leichter auslesen/überspringen, wenn der Block nicht verwendet wird.
> siehe die Verschachtelungen in der DFM |
AW: Daten per Streams in Datei schreiben
Statt 'tags' würde ich das dann gleich als XML lesen/speichern.
|
AW: Daten per Streams in Datei schreiben
Vielen Dank für eure Hilfe. Jetzt habe ich das Prinzip von Streams verstanden und kann sie richtig benutzen.
Trotzdem werde ich mich auf eine XML-basierte Speicherung festlegen, da mir die Handhabung und Wartung für dieses Projekt einfacher erscheint. Einen schönen Tag noch! |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:51 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz