Delphi Saving/Loading 动态数组失败
Delphi Saving/Loading Dynamic Arrays Failed
我认为这看起来像是“做我的作业”之类的问题,但我仍在“复制代码,使用它并尝试理解它'阶段,这是我所知道的发布该主题问题最活跃的事情。
我有一条记录:
type
Card = record
Name: string;
Up,Right,Left,Down,Mark: Single;
IDNumber: Integer;
end;
以及该记录的数组:
var
ArrayCard: array of Card;
而且我想知道这种动态数组如何成为 stored/loaded to/from 文件。
尝试使用这段代码:http://www.pascalgamedevelopment.com/showthread.php?6319-save-load-a-dynamic-array
像这样:
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;
到目前为止它似乎工作正常,但后来我尝试像这样加载它:
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;
然后我在模块中得到一个异常 EAccessViolation...
然后我也尝试了这样的事情:delphi Save and Load dynamic array
它用正确数量的项目加载数组,但它们都是空的或空白的:
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;
不要对包含 "normal" 个字符串的记录使用缓冲区操作。 shortstring
而不是 string
只是指向字符串内容的指针。因此,您的代码仅保存和加载指向字符串内容的指针,而不是字符串。如果加载的值指向无法访问的内存,您可能会遇到访问冲突。
更改您的代码以在记录中单独保存和加载变量,如下所示:
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;
更新:
你读过泛型吗?您可以使用 TList 和其中的 load/save 条记录代替动态数组:
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;
在一些帮助下,我成功地重新编写了我的代码以使其正常工作。
首先,我需要使 string
类似于 string[20]
,但无法针对 android 进行编译,因此我修改了我的记录以使用 array of char
,如下所示:
type
EventString= array [0..20] of Char;
Card = record
Name: EventString; //string[20]
Up,Right,Left,Down,Mark: Single;
IDNumber: Integer;
end;
然后我修改了 Saving/Loading 程序以使用 TFileStream 而不是 TStream 和 for 循环:
procedure TMainFrom.SaveCardsToFile;
var
Stream: TFileStream;
L: Integer;
TmpPath: string;
n: Integer;
begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');
Stream:= TFileStream.Create(TmpPath, fmCreate);
L:= Length(ArrayCard)-1;
for n:=0 to L do
begin
Stream.WriteBuffer(ArrayCard[n], SizeOf(Card)); //Saving
end;
Stream.Free;
end;
procedure TMainFrom.LoadCardsFromFile;
var
Stream: TFileStream;
L: LongWord;
TmpPath: string;
n: Integer;
begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');
Stream:= TFileStream.Create(TmpPath, fmOpenRead);
SetLength(ArrayCard, Round(Stream.Size/SizeOf(Card)));
L:= Length(ArrayCard)-1;
for n:=0 to L do
begin
Stream.ReadBuffer(ArrayCard[n], SizeOf(Card)); //Loading
ListCard.Items.Add(ArrayCard[n].Name); //Just adds card names to a listbox
end;
Stream.Free;
end;
现在一切正常。
我认为这看起来像是“做我的作业”之类的问题,但我仍在“复制代码,使用它并尝试理解它'阶段,这是我所知道的发布该主题问题最活跃的事情。
我有一条记录:
type
Card = record
Name: string;
Up,Right,Left,Down,Mark: Single;
IDNumber: Integer;
end;
以及该记录的数组:
var
ArrayCard: array of Card;
而且我想知道这种动态数组如何成为 stored/loaded to/from 文件。
尝试使用这段代码:http://www.pascalgamedevelopment.com/showthread.php?6319-save-load-a-dynamic-array 像这样:
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;
到目前为止它似乎工作正常,但后来我尝试像这样加载它:
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;
然后我在模块中得到一个异常 EAccessViolation...
然后我也尝试了这样的事情:delphi Save and Load dynamic array 它用正确数量的项目加载数组,但它们都是空的或空白的:
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;
不要对包含 "normal" 个字符串的记录使用缓冲区操作。 shortstring
而不是 string
只是指向字符串内容的指针。因此,您的代码仅保存和加载指向字符串内容的指针,而不是字符串。如果加载的值指向无法访问的内存,您可能会遇到访问冲突。
更改您的代码以在记录中单独保存和加载变量,如下所示:
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;
更新: 你读过泛型吗?您可以使用 TList 和其中的 load/save 条记录代替动态数组:
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;
在一些帮助下,我成功地重新编写了我的代码以使其正常工作。
首先,我需要使 string
类似于 string[20]
,但无法针对 android 进行编译,因此我修改了我的记录以使用 array of char
,如下所示:
type
EventString= array [0..20] of Char;
Card = record
Name: EventString; //string[20]
Up,Right,Left,Down,Mark: Single;
IDNumber: Integer;
end;
然后我修改了 Saving/Loading 程序以使用 TFileStream 而不是 TStream 和 for 循环:
procedure TMainFrom.SaveCardsToFile;
var
Stream: TFileStream;
L: Integer;
TmpPath: string;
n: Integer;
begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');
Stream:= TFileStream.Create(TmpPath, fmCreate);
L:= Length(ArrayCard)-1;
for n:=0 to L do
begin
Stream.WriteBuffer(ArrayCard[n], SizeOf(Card)); //Saving
end;
Stream.Free;
end;
procedure TMainFrom.LoadCardsFromFile;
var
Stream: TFileStream;
L: LongWord;
TmpPath: string;
n: Integer;
begin
TmpPath:= TPath.Combine(TPath.GetDocumentsPath, 'Cards.bin');
Stream:= TFileStream.Create(TmpPath, fmOpenRead);
SetLength(ArrayCard, Round(Stream.Size/SizeOf(Card)));
L:= Length(ArrayCard)-1;
for n:=0 to L do
begin
Stream.ReadBuffer(ArrayCard[n], SizeOf(Card)); //Loading
ListCard.Items.Add(ArrayCard[n].Name); //Just adds card names to a listbox
end;
Stream.Free;
end;
现在一切正常。