Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Daten per Streams in Datei schreiben (https://www.delphipraxis.net/181176-daten-per-streams-datei-schreiben.html)

delphinub23 22. Jul 2014 09:51

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:
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.
Es wäre nett, wenn mir jemand Hilfestellung geben könnte :stupid:

Photoner 22. Jul 2014 10:05

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;

Aphton 22. Jul 2014 10:23

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?!

delphinub23 22. Jul 2014 10:30

AW: Daten per Streams in Datei schreiben
 
Zitat:

Schau die mal TWriter an. Sollte genau das tun was du willst.
Danke ich schau mal rein!

Zitat:

Geht es nun konkret um eine bestimmte Architektur oder eher darum, wie du nun mit der TFileStream Klasse arbeitest?!
Es geht um die oben abgebildete Architektur.

Photoner 22. Jul 2014 10:35

AW: Daten per Streams in Datei schreiben
 
Der Vollständigkeit halber:

Mit TReader liest du das ganze wieder ein.

Klaus01 22. Jul 2014 10:57

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

delphinub23 22. Jul 2014 12:26

AW: Daten per Streams in Datei schreiben
 
Zitat:

warum hast Du die Header (a..c) dreimal als Typ deklariert?
Die Typen unterscheiden, so weit ich das gesehen habe, nicht.
Ja. Da hast du Recht. Das sind erstmal nur fiktive Typen.
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?

mjustin 22. Jul 2014 12:37

AW: Daten per Streams in Datei schreiben
 
Zitat:

Zitat von Photoner (Beitrag 1266304)
Delphi-Quellcode:
   
  Writer := TWriter.Create(AStream,$10000000);

Diese Zeile erzeugt eine TWriter Instanz, aber was machen die zehn Millionen Dollar? ;)

Aphton 22. Jul 2014 13:32

AW: Daten per Streams in Datei schreiben
 
Insgeheimer Bankeinzug bei Instanzierung..

Klaus01 22. Jul 2014 13:38

AW: Daten per Streams in Datei schreiben
 
Zitat:

Zitat von delphinub23 (Beitrag 1266322)
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?

Dann musst Du mit seek zur der Position des HeaderC springen.
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


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:26 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz