Results 1 to 4 of 4

Thread: save/load a dynamic array

  1. #1

    save/load a dynamic array

    Hi, the save-procedure saves obviously 4 records, but the load-proc loads just one from the file test.DAT.
    First thought might be that the load-proc doesn't work correctly, but the file test.MAP is read properly.
    Second thought might be the save-proc isn't well programmed. I can't figure it out. I apriciate any valid hint.
    Note: --- Manage Attachments --- isn't working so here is the code, if you need all files feel free to write to guido-lang@gmx.de
    Thanks in advance

    Code:
    unit FMain;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      StdCtrls, Buttons;
    
    const
      REC_COUNT = 4;
    
    type
      TRMap = record
         X, Y, Index: Integer;
      end;
    
      TForm1 = class(TForm)
        LoadDat: TBitBtn;
        Save: TButton;
        ListBox1: TListBox;
        LoadMap: TButton;
        procedure LoadDatClick(Sender: TObject);
        procedure SaveClick(Sender: TObject);
        procedure LoadMapClick(Sender: TObject);
      private
        { Private declarations }
       FS: TFileStream;
       ARMap: array of TRMap;
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    
    implementation
    
    {$R *.DFM}
    
    procedure TForm1.LoadDatClick(Sender: TObject);
    var
    i, FileSize: Integer;
    begin
    try
        FS := TFileStream.Create(ExtractFilePath(Paramstr(0)) + 'test.dat', fmOpenRead);
      except
        ShowMessage('Couldn''t open file.');
        Exit;
      end;
         FS.ReadBuffer(FileSize, SizeOf(FileSize));
    //     Fs.ReadBuffer(FileSize, FS.Size div SizeOf(Integer));
         SetLength(ARMap, FileSize);
        FS.ReadBuffer(ARMap[0], SizeOf(TRMap) * FileSize);
    
      Listbox1.Clear;
      for i := 0 to FileSize-1 do
      begin
      {
      FS.ReadBuffer(ARMap[i].X, SizeOf(ARMap[i].X));
      FS.ReadBuffer(ARMap[i].Y, SizeOf(ARMap[i].Y));
      FS.ReadBuffer(ARMap[i].Index, SizeOf(ARMap[i].Index));
      }
      with Listbox1.Items do
      begin
      Add(' ');
      Add(' *** Record NO: ' + InttoStr(i));
      Add(' ');
      Add('ARMap['+ InttoStr(i) +'].X: ' + InttoStr(ARMap[i].X));
      Add('ARMap['+ InttoStr(i) +'].Y: ' + InttoStr(ARMap[i].Y));
      Add('ARMap['+ InttoStr(i) +'].Index: ' + InttoStr(ARMap[i].Index));
      end; // with
      end; // for
     FS.Free;
    end; // procedure
    
    
    procedure TForm1.SaveClick(Sender: TObject);
    var
    i, liX, liY, liL: Integer;
    begin
      SetLength(ARMap, REC_COUNT);
      FS := TFileStream.Create(ExtractFilePath(Paramstr(0)) + 'test.dat', fmCreate);
      // save record number
      // FS.WriteBuffer(ARMap, SizeOf(Length(ARMap)));
    
      Listbox1.Clear;
    
      //  for i := 0 to REC_COUNT-1 do
      for i := 0 to Length(ARMap)-1 do
      begin
      ARMap[i].X := (i+1);
      ARMap[i].Y := (i+1);
      ARMap[i].Index := (i+1);
    
      with Listbox1.Items do
      begin
      Add(' ');
      Add(' *** Record NO: ' + InttoStr(i));
      Add(' ');
      Add('ARMap['+ InttoStr(i) +'].X: ' + InttoStr(ARMap[i].X));
      Add('ARMap['+ InttoStr(i) +'].Y: ' + InttoStr(ARMap[i].Y));
      Add('ARMap['+ InttoStr(i) +'].Index: ' + InttoStr(ARMap[i].Index));
      end; // with
    
    // just one rec: FS.WriteBuffer(ARMap[i], SizeOf(ARMap[i]));
    {  just one rec
      FS.WriteBuffer(ARMap[i].X, SizeOf(ARMap[i].X));
      FS.WriteBuffer(ARMap[i].Y, SizeOf(ARMap[i].Y));
      FS.WriteBuffer(ARMap[i].Index, SizeOf(ARMap[i].Index));
    }  end; // for
    
    // produces rubish which isn't loaded: FS.WriteBuffer(ARMap, SizeOf(ARMap));
    
    // just one rec:
       FS.WriteBuffer(ARMap[0], SizeOf(TRMap) * REC_COUNT);
    // just one rec: FS.WriteBuffer(Pointer(ARMap)^, Length(ARMap)* SizeOf(Integer));
    
     FS.Free;
     ARMap := nil;
    end;
    
    
    procedure TForm1.LoadMapClick(Sender: TObject);
    var
    i, FileSize: Integer;
    begin
    try
        FS := TFileStream.Create(ExtractFilePath(Paramstr(0)) + 'test.map', fmOpenRead);
     //   FS := TFileStream.Create(ExtractFilePath(Paramstr(0)) + 'test.dat', fmOpenRead);
      except
        ShowMessage('Couldn''t open file.');
        Exit;
      end;
         FS.ReadBuffer(FileSize, SizeOf(FileSize));
    //     Fs.ReadBuffer(FileSize, FS.Size div SizeOf(Integer));
         SetLength(ARMap, FileSize);
         FS.ReadBuffer(ARMap[0], SizeOf(TRMap) * FileSize);
    
      Listbox1.Clear;
      for i := 0 to FileSize-1 do
      begin
    { is working as well, but more mess
      FS.ReadBuffer(ARMap[i].X, SizeOf(ARMap[i].X));
      FS.ReadBuffer(ARMap[i].Y, SizeOf(ARMap[i].Y));
      FS.ReadBuffer(ARMap[i].Index, SizeOf(ARMap[i].Index));
    }
    
      with Listbox1.Items do
      begin
      Add(' ');
      Add(' *** Record NO: ' + InttoStr(i));
      Add(' ');
      Add('ARMap['+ InttoStr(i) +'].X: ' + InttoStr(ARMap[i].X));
      Add('ARMap['+ InttoStr(i) +'].Y: ' + InttoStr(ARMap[i].Y));
      Add('ARMap['+ InttoStr(i) +'].Index: ' + InttoStr(ARMap[i].Index));
      end; // with
      end; // for
     FS.Free;
    end;
    
    end.
    Last edited by WILL; 07-11-2010 at 07:23 PM. Reason: Please use code blocks.

  2. #2
    first of all use code tags when you're inserting large parts of the code. the way it is here will strip people of any desire to help you.
    there is a lot of commented code which is unnecesary here and I assume it confuses you as well.
    for example:
    you read the number of records here:
    FS.ReadBuffer(FileSize, SizeOf(FileSize));

    but you never write it.

    so my advice is clean you code, work through it step by step and you will surely fix all your problems.
    Last edited by Dan; 06-11-2010 at 04:27 AM.

  3. #3
    Saving/Loading a dynamic array on a stream pretty much amounts to:

    1) Writing the array elements count onto the stream.
    2) Looping through the array and writing out each element's contents, fully dereferenced, to the stream. That means the element and any part of its content has to contain no non-dereferenced pointers. Stream I/O functions expect the variables they are passed to not be or contain pointers; otherwise, you'll just store the pointer values, rather than the data in what the pointers point to.

    More or less, you're handing an untyped bucket of data to the stream write function, and it doesn't know or care what the data actually is inside the bucket. Likewise, when you read data from a stream, you pass an empty bucket and how much untyped data you want to fill it with.

    Also, the reads have to exactly mirror/balance the writes, or Things Go Bad (tm).

    With the simple record structure you are using (all well-defined static scalar types), you can just write/read it straight up. However, if you put a string or other dynamic type into that record later, you'll have to add code in the element write function to "descend into" those dynamic types and read/write them appropriately dereferenced.

    In terms of code:

    Code:
    Type
      TMyDataRecord = Record
        X,Y,Index : Integer;
        End;
    
    Var
      AMyDataArray = Array Of TMyDataRecord;
    
    Procedure WriteMyData;
    
      Var
        FS : TFileStream;
        I,iCount : Integer;
    
      Begin
      FS := TFileStream.Create(ExtractFilePath(Paramstr(0)) + 'test.dat', fmOpenWrite);
      iSize = Length(AMyDataArray);
      FS.WriteBuffer(iSize,SizeOf(iSize));
      For I := 0 To iSize - 1 Do
        FS.WriteBuffer(AMyDataArray[I],SizeOf(TMyDataRecord));
      FS.Free;
      End;
    
    Procedure ReadMyData;
    
      Var
        FS : TFileStream;
        I,iCount : Integer;
        RData : TMyDataRecord;
    
      Begin
      FS := TFileStream.Create(ExtractFilePath(Paramstr(0)) + 'test.dat', fmOpenRead);
      FS.ReadBuffer(iSize,SizeOf(iSize));
      SetLength(AMyDataArray,iSize);
      For I := 0 To iSize - 1 Do
        Begin
        FS.ReadBuffer(RData,SizeOf(TMyDataRecord));
        AMyDataArray[I] := RData;
        End;
      FS.Free;
      End;
    It has no error handling, but that's basically what you have to do.
    Last edited by Murmandamus; 06-11-2010 at 10:43 PM. Reason: Typos

  4. #4
    Like Dan pointed out, the code should only be missing this from the saving part:

    Code:
    FS.WriteBuffer(integer(REC_COUNT), SizeOf(integer));
    But if the record count is always same constant its not even necessary to read FileSize if it is always REC_COUNT.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •