德尔福保存/载入动态数组失败 [英] Delphi Saving/Loading Dynamic Arrays Failed

查看:163
本文介绍了德尔福保存/载入动态数组失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想这会看起来像'的做我的功课的之类的问题,但我仍然在'的复制code,使用它,并尝试去了解它的'阶段,这是最积极的事情,我知道张贴这一主题的问题。

我有一个记录:

 键入
  卡=记录
    名称:字符串;
    上,右,左,下,马克:单;
    IDNumber中:整数;
    结束;

和该记录的数组:

  VAR
  ArrayCard:卡的阵列;

和我想知道如何能这样的一个动态数组被存储/从文件加载到/。

使用这块code的尝试:的http://www.pascalgamedevelopment.com/showthread.php?6319-save-load-a-dynamic-array
像这样的:

 程序TMainFrom.WriteMyData;  瓦尔
    FS:TFileStream的;
    我,iSize:整数;
    TmpPath:字符串;  开始
    TmpPath:= TPath.Combine(TPath.GetDocumentsPath,'Cards.dat');
    FS:= TFileStream.Create(TmpPath,fmOpenWrite);
    iSize:=长度(ArrayCard);
    FS.WriteBuffer(iSize,一下SizeOf(iSize));
  因为我:= 0〜iSize - 1待办事项
    FS.WriteBuffer(ArrayCard [I]中,一下SizeOf(卡));
  FS.Free;
  结束;

这是它似乎工作至今,但后来我尝试加载它是这样的:

 程序TMainFrom.ReadMyData;  瓦尔
    FS:TFileStream的;
    我,iSize:整数;
    TmpPath:字符串;
    TempCard:卡;  开始
    TmpPath:= TPath.Combine(TPath.GetDocumentsPath,'Cards.dat');
    FS:= TFileStream.Create(TmpPath,fmOpenRead);
    FS.ReadBuffer(iSize,一下SizeOf(iSize));
    SetLength函数(ArrayCard,iSize);
  因为我:= 0〜iSize - 1待办事项
    开始
    FS.ReadBuffer(TempCard,一下SizeOf(卡));
    ArrayCard [I]:= TempCard; //它打破了这里...... [观察清单:TempCard价值无法访问
    结束;
  FS.Free;
  结束;

和我得到了模块异常EAccessViolation ...

然后我也尝试过这样的事情:德尔福保存和载入动态数组
它加载的项目的正确数量的数组,但它们都是空的或空白:

 程序TMainFrom.SaveCardsToFile;
VAR
  流:TStream;
  L:整数;
  TmpPath:字符串;
  ICOUNT:整数;
  STRM:内存流;开始
  TmpPath:= TPath.Combine(TPath.GetDocumentsPath,'Cards.bin');  流:= TFileStream.Create(TmpPath,fmCreate);
  L:=长度(ArrayCard);
  Stream.WriteBuffer(L,一下SizeOf(L));
  Stream.WriteBuffer(指针(ArrayCard)^,L *一下SizeOf(卡));
  Stream.Free;
结束;程序TMainFrom.LoadCardsFromFile;
VAR
  流:TStream;
  L:长字;
  TmpPath:字符串;开始
  TmpPath:= TPath.Combine(TPath.GetDocumentsPath,'Cards.bin');  流:= TFileStream.Create(TmpPath,fmOpenRead);
  Stream.ReadBuffer(L,一下SizeOf(L));
  SetLength函数(ArrayCard,L);
  Stream.ReadBuffer(指针(ArrayCard)^,L *一下SizeOf(卡));
  Stream.Free;
结束;


解决方案

不要使用包含正常的字符串记录缓冲区操作。相反 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;

这篇关于德尔福保存/载入动态数组失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆