Delphi:向文件中写入/读取变量和记录 [英] Delphi: write/read variables and records to/from file

查看:88
本文介绍了Delphi:向文件中写入/读取变量和记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在XE8中处理我的项目时,我不得不保存和读取自定义项目文件,这些文件存储变量和不同类型的记录.最初,我解决该问题的方法似乎奏效,但在实际项目中却证明是错误的.

Working on my project in XE8, i've faced a necessity to save and read custom project files, which store variables and records of different types. Initially, my approach to solving that problem seemed to work, but in actual project it proved faulty.

我创建文件的方法,用于存储类别"记录:

My method for creating a file, storing a "Categories" record:

var 
SavingStream: TFileStream;
         i,j: Integer;
begin
SavingStream:=TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
SavingStream.Position:=0;
i:=Length(Categories);                 **// storing size of an array in a temp variable**
SavingStream.WriteBuffer(i,SizeOf(i)); **// for some reason i couldn't save it directly**
for i:=0 to Length(Categories)-1 do
   begin         
   **{ String }**
   SavingStream.WriteBuffer(Categories[i].Name,SizeOf(Categories[i].Name));  
   **{ Integer }**   
   SavingStream.WriteBuffer(Categories[i].ID,SizeOf(Categories[i].ID));
   **{ Boolean }**
   SavingStream.WriteBuffer(Categories[i].Default,SizeOf(Categories[i].Default))
   **{ Same routine for dynamic array }**
   j:=Length(Categories[i].ChildrenType);
   SavingStream.WriteBuffer(j,SizeOf(j));
   if j>=1 then for j:=0 to Length(Categories[i].ChildrenType)-1 do SavingStream.WriteBuffer(Categories[i].ChildrenType[j],SizeOf(Categories[i].ChildrenType[j]));
   end;
end;

然后阅读:

var 
SavingStream: TFileStream;
         i,j: Integer;
begin
try
SavingStream.ReadBuffer(i,SizeOf(i));
SetLength(Categories,i);
for i:=0 to Length(Categories)-1 do
   begin
   SavingStream.ReadBuffer(Categories[i].Name,SizeOf(Categories[i].Name));     
   SavingStream.ReadBuffer(Categories[i].ID,SizeOf(Categories[i].ID));         
   SavingStream.ReadBuffer(Categories[i].Default,SizeOf(Categories[i].Default));
   SavingStream.ReadBuffer(j,SizeOf(j));
   SetLength(Categories[i].ChildrenType,j);
   if j>=1 then for j:=0 to Length(Categories[i].ChildrenType)-1 do SavingStream.ReadBuffer(Categories[i].ChildrenType[j],SizeOf(Categories[i].ChildrenType[j]));
   end;
finally
SavingStream.Free;
end;

主要问题之一是我不完全了解此方法背后的逻辑.据我了解,SizeOf(i)基本上是说要获取原本同类文件中的某个部分,并将其作为变量的值.但是,如何存储大小可变的字符串和数组?我知道可以在记录本身中限制它的大小,但是我不想限制某些字符串变量.

One of the main problems is that i don't entirely understand the logic behind this method. It is my understanding, that SizeOf(i) is basically saying to take a certain part of an otherwise homogeneous file and taking it as a variable's value. But how do i store strings and arrays with a variable size? I know it's possible to limit it's size in the record itself, but there are certain string variables that i don't want to be limited in.

因此,我需要您的建议,我使用的方法是否有效,以及如何使其在我的特定情况下有效.也许有更好的方法来存储此信息?请记住,我必须存储各种各样的不同类型,包括图像.

Thus i need your advice whether the method i'm using is any good and how can i make it work in my specific case. Maybe there is a better way to store this information? Keep in mind, that i have to store a vast range of different types, including images.

提前.

推荐答案

您需要将可变长度数据(例如字符串)序列化为不包含指向其他内存地址的任何指针的平面格式.

You need to serialize variable-length data, like strings, into a flat format that does not contain any pointers to other memory addresses.

尝试这样的事情:

procedure WriteIntegerToStream(Stream: TStream; Value: Integer);
begin
  Stream.WriteBuffer(Value, Sizeof(Value));
end;

procedure WriteBooleanToStream(Stream: TStream; Value: Boolean);
begin
  Stream.WriteBuffer(Value, Sizeof(Value));
end;

procedure WriteStringToStream(Stream: TStream; const Value: String);
var
  S: UTF8String;
  Len: Integer;
begin
  S := UTF8String(Value);
  Len := Length(S);
  WriteIntegerToStream(Stream, Len);
  Stream.WriteBuffer(PAnsiChar(S)^, Len);
end;

var 
  SavingStream: TFileStream;
  i, j: Integer;
begin
  SavingStream := TFileStream.Create('SAVE.test', fmCreate or fmOpenWrite or fmShareDenyWrite);
  try
    WriteIntegerToStream(SavingStream, Length(Categories));
    for i := 0 to Length(Categories)-1 do
    begin         
      WriteStringToStream(SavingStream, Categories[i].Name);
      WriteIntegerToStream(SavingStream, Categories[i].ID);
      WriteBooleanToStream(SavingStream, Categories[i].Default);
      WriteIntegerToStream(SavingStream, Length(Categories[i].ChildrenType));
      for j := 0 to Length(Categories[i].ChildrenType)-1 do
      begin
        // write ChildrenType[j] data to SavingStream as needed...
      end;
  finally
    SavingStream.Free;
  end;
end;

然后,当回读文件时,您可以执行类似的操作:

Then you can do something similar when reading the file back:

function ReadIntegerFromStream(Stream: TStream): Integer;
begin
  Stream.ReadBuffer(Result, Sizeof(Result));
end;

function ReadBooleanFromStream(Stream: TStream): Boolean;
begin
  Stream.ReadBuffer(Result, Sizeof(Result));
end;

function ReadStringFromStream(Stream: TStream): String;
var
  S: UTF8String;
  Len: Integer;
begin
  Len := ReadIntegerFromStream(Stream);
  SetLength(S, Len);
  Stream.ReadBuffer(PAnsiChar(S)^, Len);
  Result := String(S);
end;

var 
  LoadingStream: TFileStream;
  i, j: Integer;
begin
  LoadingStream := TFileStream.Create('SAVE.test', fmOpenRead or fmShareDenyWrite);
  try
    i := ReadIntegerFromStream(LoadingStream);
    SetLength(Categories, i);
    for i := 0 to Length(Categories)-1 do
    begin
      Categories[i].Name := ReadStringFromStream(LoadingStream);
      Categories[i].ID := ReadIntegerFromStream(LoadingStream);
      Categories[i].Default := ReadBooleanFromStream(LoadingStream);
      j := ReadIntegerFromStream(LoadingStream);
      SetLength(Categories[i].ChildrenType, j);
      for j := 0 to Length(Categories[i].ChildrenType)-1 do
      begin
        // read ChildrenType[j] data from LoadingStream as needed...
      end;
    end;
  finally
    LoadingStream.Free;
  end;
end;

这篇关于Delphi:向文件中写入/读取变量和记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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