且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

德尔福保存/载入动态数组失败

更新时间:2023-02-02 21:32:03

不要使用包含正常的字符串记录缓冲区操作。相反 ShortString短字符串是唯一的指向字符串内容。所以,你的code保存和加载唯一的指向字符串的内容,而不是字符串。如果装载值指向不可访问内存即可获得访问冲突。
更改code分开保存和记录加载变量,像这样的:

 类型
  TmyRec =记录
    STR:字符串;
    我:整数;
    程序SaveToStream(流:TStream);
    程序LoadFromStream(流:TStream);
  结束;{} myRec程序TmyRec.LoadFromStream(流:TStream);
VAR
  STRLEN:整数;
  STRBUF:TB的;
开始
  Stream.Read(STRLEN,一下SizeOf(整数));
  SetLength函数(STRBUF,strlen的);
  Stream.Read(STRBUF,strlen的);  STR:= TEncoding.UTF8.GetString(STRBUF);  Stream.Read(我,一下SizeOf(整数));
结束;程序TmyRec.SaveToStream(流:TStream);
VAR
  STRBUF:TB的;
  STRLEN:整数;
开始
  //直集编码类型有助于避免不同平台的问题。
  //例如,Windows使用UCS2,Android和iOS - UTF8编码
  STRBUF:= TEncoding.UTF8.GetBytes(STR);
  STRLEN:=长度(STRBUF);  Stream.Write(STRLEN,一下SizeOf(整数));
  Stream.Write(STRBUF,strlen的);
  Stream.Write(我,一下SizeOf(整数));
结束;

更新
你了解泛型?相反,动态数组,你可以使用从TList并加载/保存记录的:

 类型
  TmyRecList =等级(从TList< TmyRec>)
  上市
    程序SaveToStream(流:TStream);
    程序LoadFromStream(流:TStream);
  结束;{} TmyRecList程序TmyRecList.LoadFromStream(流:TStream);
VAR
  莱恩:整数;
  我:整数;
  REC:TmyRec;
开始
  明确;  Stream.Read(LEN,一下SizeOf(整数));  对于i:= 0至莱恩-1做
    开始
      Rec.LoadFromStream(流);
      添加(REC);
    结束;
结束;程序TmyRecList.SaveToStream(流:TStream);
VAR
  我:整数;
开始
  Stream.Write(计数,一下SizeOf(整数));
  对于i:= 0开始计数1做
    项目[I] .SaveToStream(流);
结束;程序THeaderFooterForm.FormCreate(发件人:TObject的);
VAR
  流:TStream;
  REC:TmyRec;
  recList:TmyRecList;
开始
  流:= TMemoryStream.Create;
  尝试
    recList:= TmyRecList.Create;
    尝试
      rec.str:='示范文本;
      rec.i:= 123;
      recList.Add(REC);      rec.str:='其他文本';
      rec.i:= 234;
      recList.Add(REC);      recList.SaveToStream(流);
      Stream.Seek(0,soBeginning);      recList.LoadFromStream(流);
      ShowMessage('这是第二个记录STR值:'+ recList [1]名为.str);
    最后
      recList.Free;
    结束;
  最后
    Stream.Free;
  结束;

I think this will look like 'do my homework' kind of a question, but I'm still at the 'copy code, use it and try to understand it' phase, and this is the most active thing I know of for posting questions of this theme.

I have a record:

type
  Card = record
    Name: string;
    Up,Right,Left,Down,Mark: Single; 
    IDNumber: Integer;
    end;

And array of that record:

var
  ArrayCard: array of Card;

And I wanted to know how can a dynamic array of this kind be stored/loaded to/from a file.

Tried using this piece of code: http://www.pascalgamedevelopment.com/showthread.php?6319-save-load-a-dynamic-array like this:

Procedure TMainFrom.WriteMyData;

  Var
    FS : TFileStream;
    I,iSize : Integer;
    TmpPath: string;

  Begin
    TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.dat');
    FS := TFileStream.Create(TmpPath, fmOpenWrite);
    iSize:= Length(ArrayCard);
    FS.WriteBuffer(iSize,SizeOf(iSize));
  For I := 0 To iSize - 1 Do
    FS.WriteBuffer(ArrayCard[I],SizeOf(Card));
  FS.Free;
  End;

An it seems to work so far, but then I try to load it like this:

Procedure TMainFrom.ReadMyData;

  Var
    FS : TFileStream;
    I,iSize : Integer;
    TmpPath: string;
    TempCard : Card;

  Begin
    TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.dat');
    FS := TFileStream.Create(TmpPath, fmOpenRead);
    FS.ReadBuffer(iSize,SizeOf(iSize));
    SetLength(ArrayCard,iSize);
  For I := 0 To iSize - 1 Do
    Begin
    FS.ReadBuffer(TempCard,SizeOf(Card));
    ArrayCard[I] := TempCard; //It Breaks Here...The Watch List: TempCard Inaccessible value
    End;
  FS.Free;
  End;

And I get a Exception EAccessViolation in module...

Then I also tried something like this: delphi Save and Load dynamic array It loads the array with the correct amount of items, but they are all empty or blank:

procedure TMainFrom.SaveCardsToFile;
var
  Stream: TStream;
  L: Integer;
  TmpPath: string;
  ICount: Integer;
  strm: TMemoryStream;

begin
  TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');

  Stream:= TFileStream.Create(TmpPath, fmCreate);
  L:= Length(ArrayCard);
  Stream.WriteBuffer(L, SizeOf(L));
  Stream.WriteBuffer(Pointer(ArrayCard)^, L * SizeOf(Card));
  Stream.Free;
end;

procedure TMainFrom.LoadCardsFromFile;
var
  Stream: TStream;
  L: LongWord;
  TmpPath: string;

begin
  TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');

  Stream:= TFileStream.Create(TmpPath, fmOpenRead);
  Stream.ReadBuffer(L, SizeOf(L));
  SetLength(ArrayCard, L);
  Stream.ReadBuffer(Pointer(ArrayCard)^, L * SizeOf(Card));
  Stream.Free;
end;

Do not use buffer operations with records which contains "normal" strings. Instead of shortstring, string is only pointer to string content. So, your code saves and loads only pointer to string content, not string. You can get Access Violation if loaded value points to unreachable memory. Change your code to separately save and load variables in record, like this:

type
  TmyRec = record
    str: string;
    i: integer;
    procedure SaveToStream(Stream: TStream);
    procedure LoadFromStream(Stream: TStream);
  end;

{ myRec }

procedure TmyRec.LoadFromStream(Stream: TStream);
var
  strLen: integer;
  strBuf: TBytes;
begin
  Stream.Read(strLen, SizeOf(Integer));
  SetLength(strBuf, strLen);
  Stream.Read(strBuf, strLen);

  str:=TEncoding.UTF8.GetString(strBuf);

  Stream.Read(i, SizeOf(Integer));
end;

procedure TmyRec.SaveToStream(Stream: TStream);
var
  strBuf: TBytes;
  strLen: integer;
begin
  // direct set encoding type helps to avoid problems with different platforms.
  // for example, Windows uses UCS2, Android and iOS - UTF8 encoding
  strBuf:=TEncoding.UTF8.GetBytes(str);
  strLen:=Length(strBuf);

  Stream.Write(strLen, SizeOf(Integer));
  Stream.Write(strBuf, strLen);
  Stream.Write(i, SizeOf(Integer));
end;

Update: do you read about generics? Instead of dynamic array, you can use TList and load/save records in it:

type
  TmyRecList = class(TList<TmyRec>)
  public
    procedure SaveToStream(Stream: TStream);
    procedure LoadFromStream(Stream: TStream);
  end;

{ TmyRecList }

procedure TmyRecList.LoadFromStream(Stream: TStream);
var
  Len: integer;
  i: Integer;
  rec: TmyRec;
begin
  Clear;

  Stream.Read(Len, SizeOf(integer));

  for i := 0 to Len-1 do
    begin
      Rec.LoadFromStream(Stream);
      Add(rec);
    end;
end;

procedure TmyRecList.SaveToStream(Stream: TStream);
var
  i: Integer;
begin
  Stream.Write(Count, SizeOf(Integer));
  for i := 0 to Count-1 do
    Items[i].SaveToStream(Stream);
end;

procedure THeaderFooterForm.FormCreate(Sender: TObject);
var
  Stream: TStream;
  rec: TmyRec;
  recList: TmyRecList;
begin
  Stream:=TMemoryStream.Create;
  try
    recList:=TmyRecList.Create;
    try
      rec.str:='sample text';
      rec.i:=123;
      recList.Add(rec);

      rec.str:='another text';
      rec.i:=234;
      recList.Add(rec);

      recList.SaveToStream(Stream);
      Stream.Seek(0, soBeginning);

      recList.LoadFromStream(Stream);
      ShowMessage('this is str value in second record: ' + recList[1].str);
    finally
      recList.Free;
    end;
  finally
    Stream.Free;
  end;