簡體   English   中英

Delphi:向文件中寫入/讀取變量和記錄

[英]Delphi: write/read variables and records to/from file

在XE8中處理我的項目時,我不得不保存和讀取自定義項目文件,該文件存儲變量和不同類型的記錄。 最初,我解決該問題的方法似乎奏效,但在實際項目中卻證明是錯誤的。

我創建文件的方法,存儲“類別”記錄:

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)基本上是在說要獲取本來是同質文件的某個部分並將其作為變量的值。 但是,如何存儲大小可變的字符串和數組? 我知道有可能在記錄本身中限制它的大小,但是我不想限制某些字符串變量。

因此,我需要您的建議,我使用的方法是否有效,以及如何使其在我的特定情況下有效。 也許有更好的方法來存儲此信息? 請記住,我必須存儲各種各樣的不同類型,包括圖像。

提前謝謝。

您需要將可變長度數據(例如字符串)序列化為不包含指向其他內存地址的任何指針的平面格式。

嘗試這樣的事情:

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;

然后,您可以在讀回文件時執行類似的操作:

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM