AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Record mit dyn.Array speichern

Ein Thema von Sinderion · begonnen am 5. Jan 2012 · letzter Beitrag vom 12. Jan 2012
Antwort Antwort
Benutzerbild von olee
olee

Registriert seit: 16. Feb 2008
Ort: Boppard
540 Beiträge
 
Turbo Delphi für Win32
 
#1

AW: Record mit dyn.Array speichern

  Alt 6. Jan 2012, 10:55
Das ist genau das, was ich meinte.
Aber das ist auch keine wirklich saubere Art um ein record zu speichern.
Ich selber verwende eigentlich immer einen class-helper für TStream mit Methoden wie WriteInteger und ReadInteger, welche ich dann für jeden Stream nutzen kann.
Ich werde gleich mal ein Beispiel posten.
Leider geht das gerade nicht gut, da ich mobil online bin.

MFG
Björn Zeutzheim
Codename: Performancepumpe
  Mit Zitat antworten Zitat
Benutzerbild von olee
olee

Registriert seit: 16. Feb 2008
Ort: Boppard
540 Beiträge
 
Turbo Delphi für Win32
 
#2

AW: Record mit dyn.Array speichern

  Alt 6. Jan 2012, 12:02
So nun mal richtig.

Dies ist der Stream-helper den ich immer verwende:
Delphi-Quellcode:
  TStreamHelper = class helper for TStream
    function ReadBoolean: Boolean;
    procedure WriteBoolean(v: Boolean);
    Function ReadByte : Byte;
    Procedure WriteByte(v : Byte);
    Function ReadWord : Word;
    Procedure WriteWord(v : Word);
    function ReadInteger: Integer;
    procedure WriteInteger(v: Integer);
    function ReadCardinal: Cardinal;
    procedure WriteCardinal(v: Cardinal);
    function ReadInt64: Int64;
    procedure WriteInt64(v: Int64);
    function ReadSingle: Single;
    procedure WriteSingle(v: Single);
    function ReadDouble: Double;
    procedure WriteDouble(v: Double);
    function ReadString: AnsiString;
    procedure WriteString(const v: AnsiString);
    function ReadChars(count: Integer): AnsiString;
    procedure WriteChars(const v: AnsiString; count: Integer);
    function ReadWideString: WideString;
    procedure WriteWideString(const v: WideString);
    function ReadWideChars(count: Integer): WideString;
    procedure WriteWideChars(const v: WideString; count: Integer);
    function StreamMove(Src, Dst, Count: Int64): Int64;
  end;

implementation

{$ifdef VER140}{$REGION 'TStreamHelper'}{$endif}

function TStreamHelper.StreamMove(Src, Dst, Count: Int64): Int64;
var
  Buffer : Array [0..1024 * 32 - 1] of Byte;
  ByteCount : Integer;
  ReadBytes : Integer;
begin
  Result := 0;
  while Count > 0 do
  begin
    If Count > length(Buffer) then
      ByteCount := length(Buffer)
    else
      ByteCount := Count;

    Position := Src;
    ReadBytes := Read(Buffer, ByteCount);
    If ReadBytes <> ByteCount then
      raise Exception.Create('Abnormal exception. Could not read desired bytes from the file.');

    Position := Dst;
    ByteCount := Write(Buffer, ReadBytes);
    If ByteCount <> ReadBytes then
      raise Exception.Create('Abnormal exception. Could not write desired bytes to the file.');

    Count := Count - ByteCount;
    Result := Result + ByteCount;
    Src := Src + ByteCount;
    Dst := Dst + ByteCount;
  end;
End;

Function TStreamHelper.ReadBoolean : Boolean;
Begin
  Read(Result, 1);
End;

Procedure TStreamHelper.WriteBoolean(v : Boolean);
Begin
  Write(v, 1);
End;

Function TStreamHelper.ReadByte : Byte;
Begin
  Read(Result, 1);
End;

Procedure TStreamHelper.WriteByte(v : Byte);
Begin
  Write(v, 1);
End;

Function TStreamHelper.ReadWord : Word;
Begin
  Read(Result, 2);
End;

Procedure TStreamHelper.WriteWord(v : Word);
Begin
  Write(v, 2);
End;

Function TStreamHelper.ReadInteger : Integer;
Begin
  Read(Result, 4);
End;

Procedure TStreamHelper.WriteInteger(v : Integer);
Begin
  Write(v, 4);
End;

Function TStreamHelper.ReadCardinal : Cardinal;
Begin
  Read(Result, 4);
End;

Procedure TStreamHelper.WriteCardinal(v : Cardinal);
Begin
  Write(v, 4);
End;

function TStreamHelper.ReadInt64: Int64;
begin
  Read(Result, 8);
end;

procedure TStreamHelper.WriteInt64(v: Int64);
begin
  Write(v, 8);
end;

Function TStreamHelper.ReadSingle : Single;
Begin
  Read(Result, 4);
End;

Procedure TStreamHelper.WriteSingle(v : Single);
Begin
  Write(v, 4);
End;

function TStreamHelper.ReadDouble: Double;
begin
  Read(Result, 8);
end;

procedure TStreamHelper.WriteDouble(v: Double);
begin
  Write(v, 8);
end;

Procedure TStreamHelper.WriteString(Const v : AnsiString);
Var Len : Integer;
Begin
  Len := Length(v);
  Write(Len, SizeOf(Len));
  Write(PChar(v)^, Len);
End;

Function TStreamHelper.ReadString: AnsiString;
Var Len : Integer;
Begin
  Read(Len, SizeOf(Len));
  //If len > 20000 Then exit;
  SetLength(Result, Len);
  Read(PChar(Result)^, Len);
End;

Procedure TStreamHelper.WriteChars(Const v : AnsiString; count: Integer);
Begin
  Write(PAnsiChar(v)^, count);
End;

Function TStreamHelper.ReadChars(count: Integer) : AnsiString;
Begin
  SetLength(Result, count);
  Read(PAnsiChar(Result)^, count);
End;

Procedure TStreamHelper.WriteWideString(Const v : WideString);
Var Len : Integer;
Begin
  Len := Length(v);
  Write(Len, SizeOf(Len));
  Write(PWideChar(v)^, Len * 2);
End;

Function TStreamHelper.ReadWideString: WideString;
Var Len : Integer;
Begin
  Read(Len, SizeOf(Len));
  //If len > 20000 Then exit;
  SetLength(Result, Len);
  Read(PWideChar(Result)^, Len * 2);
End;

Procedure TStreamHelper.WriteWideChars(Const v : WideString; count: Integer);
Begin
  Write(PWideChar(v)^, count * 2);
End;

Function TStreamHelper.ReadWideChars(count: Integer) : WideString;
Begin
  SetLength(Result, count);
  Read(PWideChar(Result)^, count * 2);
End;

{$ifdef VER140}{$ENDREGION}{$endif}
Zum speichern eines Records verwende ich dann folgende Implementation:
Delphi-Quellcode:
  PTestData = ^TTestData;
  TTestData = record
    MyInteger : Integer;
    MyString : AnsiString;
    MyWString : WideString;
    MyIntArray : Array of Integer;
    MyRecordArray : Array of PTestData;
    procedure SaveToStream(Stream: TStream);
  end;

implementation

procedure TTestData.SaveToStream(Stream: TStream);
var
  i: Integer;
begin
  Stream.WriteInteger(MyInteger);
  Stream.WriteAString(MyString);
  Stream.WriteWString(MyWString);

  Stream.WriteInteger(length(MyIntArray));
  Stream.Write(MyIntArray[0], length(MyIntArray) * SizeOf(MyIntArray[0]));

  Stream.WriteInteger(length(MyRecordArray));
  for i := 0 to high(MyRecordArray) do
    MyRecordArray[i]^.SaveToStream(Stream);
end;

procedure TTestData.LoadFromStream(Stream: TStream);
var
  i: Integer;
begin
  MyInteger := Stream.ReadInteger;
  MyString := Stream.ReadAString;
  MyWString := Stream.ReadWString;

  SetLength(MyIntArray, Stream.ReadInteger);
  Stream.Read(MyIntArray[0], length(MyIntArray) * SizeOf(MyIntArray[0]));

  SetLength(MyRecordArray, Stream.ReadInteger);
  for i := 0 to high(MyRecordArray) do
    MyRecordArray[i]^.LoadFromStream(Stream);
end;
Das sieht nicht nur um einiges ordentlicher aus, sondern ist auch noch schnell programmiert und viel sicherer.
Björn Zeutzheim
Codename: Performancepumpe
  Mit Zitat antworten Zitat
Keldorn

Registriert seit: 6. Mär 2003
Ort: Meißen
876 Beiträge
 
Delphi 10.1 Berlin Professional
 
#3

AW: Record mit dyn.Array speichern

  Alt 6. Jan 2012, 15:42
Hallo,

würde ich genauso machen. Den classhelper würdest Du noch nicht mal unbedingt benötigen, Delphi hat das schon fast alles onboard (Twriter/Treader).
Persönlich würde ich auch die unterrecords nach der gleichen Methode, allerdings jedes Element dann einzeln in den stream schreiben.

bsp:
Delphi-Quellcode:
Var Writer:Twriter;
  begin
    writer:= Twriter.create(Stream,4096);
    try
      begin
        //version schreiben
        Writer.writeinteger(aktuelleVersionsNummer);

        Writer.WriteString(blub1);
        Writer.WriteString(blub2);
        Writer.WriteInteger(blubbi);
        ...
        Writer.WriteInteger(Länge des dyn. arrays);
        for i:=0 to ... usw
          Writer.writestring(meinrecord.xyz
        ...
      end;
    finally
      writer.free;
    end;
Ich finde das recht angenehm, wenn man das dann als Klasse macht und jede Klasse eine savetostream und loadfromstream- Methode hat.
Die Versionsnummer lege in einer unit fortlaufen ab. Bei savetostream wird immer die aktuelle Version geschrieben und dann kann man die bei loadfromstream auch auswerten
Delphi-Quellcode:
with reader do
  begin
    //Version lesen
    Datenversion := readinteger;

    blub:=Readstring;
    if Datenversion>=Version155 then
      begin
        blub2:=readstring;
      end;
    blubbi:=readinteger;
  end;
blub2 ist halt irgendwann mal dazugekommen. Auf diese Weise kann man schnell eine Abwärtskompatibiliät erreichen, egal mit welcher alten Version des Programms die Daten gespeichert wurden, die neue bekommt sie immer wieder+korrekt auf. Und ohne, daß Du dir beim Programmieren da große Kopfzerbrechen bereitest.
Wenn das dyn. Array komplett geschrieben wird, würde ja hier bei Änderungen ein recht hoher Aufwand entstehen, alte gespeicherte Daten noch zu lesen und umzuwandeln. Lieber da gleich etwas mehr Code schreiben (der trotzdem sehr übersichtlich und verständlich ist), als dann bei Änderungen eine code zu haben, wo keiner mehr durchsieht.

Andere Frage: würde man sich nicht hier auch evtl Zukunftsprobleme einhandeln, wenn die Daten komplett geschrieben werden und man auf die Datentypen nicht aufpaßt (z.B. string verwendet)?
Die Treader und Twriter sollten da ja entsprechend aufgebaut sein, daß hier nix in die Hose geht.
Ich hatte das hier ja schon mal gefragt und hoffe das paßt auch so. Probieren kann ich es noch nicht, da mein PC eine effektive Methode gefunden hat, das ich mein Delphi XE2 nicht installieren kann

Gruß Frank

Lükes Grundlage der Programmierung:
Es wird nicht funktionieren
(Murphy)
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#4

AW: Record mit dyn.Array speichern

  Alt 6. Jan 2012, 17:57
Ich würde das auch nicht so krank machen, wie von mir gepostet. Aber es ging um die optimale Möglichkeit, wenn dynamische Arrays im Spiel sind.

Nebenbei würde ich die Versionierung über eine Classfactory lösen, ansonsten sieht die Leseroutine nach einigen Versionen verdammt krank aus und wird im Laufe der Jahre immer schlümmer.
  Mit Zitat antworten Zitat
Sinderion

Registriert seit: 23. Nov 2007
Ort: Oberösterreich
19 Beiträge
 
Delphi XE7 Architect
 
#5

AW: Record mit dyn.Array speichern

  Alt 6. Jan 2012, 19:25
Ok, schon mal danke für die Vorschläge, werde mir das Ganze in den nächsten Tagen mal anschauen.
Daniel
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.154 Beiträge
 
Delphi 10.3 Rio
 
#6

AW: Record mit dyn.Array speichern

  Alt 7. Jan 2012, 03:20
Ich möchte mal den Begriff VariantenRecord in den Raum werfen...

ODER

Anstatt den Helper zu nehmen würde ich sowieso einen Art bevorzugen:

Delphi-Quellcode:
    Procedure Schreibe(V:Integer);overload;
    Procedure Schreibe(V:Boolean);overload;
    Procedure Schreibe(V:Byte);overload;
    Procedure Schreibe(V:AnsiChar);overload;
    Procedure Schreibe(V:Word);overload;
    Procedure Schreibe(V:Int64);overload;
    Procedure Schreibe(V:Shortstring);overload;
    Procedure Schreibe(V:AnsiString);overload;
    Procedure Schreibe(V:TDateTime);overload;
    Procedure Schreibe(V:TStream);overload;
    Procedure Schreibe(Var ALL;Size:Integer);overload;

    Function Lese(Var V:Integer):boolean;overload;
    Function Lese(Var V:Boolean):boolean;overload;
    Function Lese(Var V:Byte):boolean;overload;
    Function Lese(Var V:AnsiChar):boolean;overload;
    Function Lese(Var V:Word):boolean;overload;
    Function Lese(Var V:Int64):boolean;overload;
    Function Lese(Var V:Shortstring):boolean;overload;
    Function Lese(Var V:AnsiString):boolean;overload;
    Function Lese(Var V:TDateTime):boolean;overload;
    Function Lese(Var V:TStream):boolean;overload;
    Function Lese(Var ALL;Size:Integer):boolean;overload;
Mavarik
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#7

AW: Record mit dyn.Array speichern

  Alt 7. Jan 2012, 08:23
Ich möchte mal den Begriff VariantenRecord in den Raum werfen...
Was soll man damit anfangen?

Zitat:
Anstatt den Helper zu nehmen würde ich sowieso einen Art bevorzugen:
Und wo ist da der Unterschied?
  Mit Zitat antworten Zitat
Sinderion

Registriert seit: 23. Nov 2007
Ort: Oberösterreich
19 Beiträge
 
Delphi XE7 Architect
 
#8

AW: Record mit dyn.Array speichern

  Alt 12. Jan 2012, 19:05
So, endlich wieder ein wenig Zeit um weiterzumachen.

Ich habe mir jetzt einmal die Writer/Reader-Klassen angesehen und denke, dass die durchaus meinem Zwecke gerecht werden. Werde mich da noch weiter einarbeiten. Soweit ich das sehe, ist olees Methode prinzipiell das selbe, nur das mir hier der Writer/Reader einen Teil der Arbeit abnimmt.
Noch nicht ganz klar ist mir beim Writer wie ich Daten zu einer bestehenden Datei hinzufügen kann. Liege ich soweit richtig, dass ich da "Position" entsprechend setzten muss? Bin gerade am herumexperimentieren (erstmal nur mit Integern), aber es will nicht sorecht funktionieren.
Daniel
  Mit Zitat antworten Zitat
Antwort Antwort

 
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:18 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-2025 by Thomas Breitkreuz