Delphi-使用流将记录保存到文件

时间:2019-01-03 22:41:40

标签: delphi stream

Delphi Tokyo-我有一个参数文件,需要从磁盘保存(然后加载)。参数是一系列记录对象。有一个HEADER记录,然后是多个COMMAND记录。这些是真实记录(即类型=记录)。 HEADER记录中包含String,Boolean,Integer和TStringList类型。我保存了,看来工作正常,但是当我加载时,TStringList之后的任何内容都会导致Stream读取错误。例如...

type tEDP_PROJ = record
   Version : Integer;
   Name: String;
    ...
   ColList1: TStringList;
   ColList2: TStringList;
   ReadyToRun : Boolean;
   ...
 end;

当我阅读ReadyToRun时,出现流读取错误。如果我在TStringList之前(在SAVE和LOAD例程上)将其移动,则ReadyToRun将正确加载,但是TStringList之后的内容将导致错误。有趣的是,ColList2可以很好地加载(即使它不是第一个TStringList)。

保存TStringList时,我正在指定Encoding方法。

...
ColList1.SaveToStream(SavingStream, TEncoding.Unicode);
ColList2.SaveToStream(SavingStream, TEncoding.Unicode);

从(文件)流加载时,我使用的是相同的编码。

...
   ColList1.LoadFromStream(SavingStream, TEncoding.Unicode);
   ColList2.LoadFromStream(SavingStream, TEncoding.Unicode);

请注意,当我创建StringList时,我只是在进行标准创建...

ColList1 := TStringList.Create;

保存和加载时,我遵循的是雷米给here ...提供的示例。

TStringList似乎正在改变流读取非TStringList类型的方式...我该怎么做才能解决此问题?

1 个答案:

答案 0 :(得分:4)

您为什么要使用TEncoding.UnicodeTEncoding.UTF8会更有意义。

无论如何,这不是编码问题。您尝试做的事情根本不会像您尝试做的那样工作,因为TStrings数据是可变长度的,需要进行相应的处理。但是,TStrings不会将任何类型的终止定界符或大小信息保存到输出流。在流中加载时,TStrings.LoadFromStream()只需读取整个流(嗯,无论如何,在当前Position和流结束之间的所有内容)。这就是为什么在尝试在任何TStrings数据之后读取/写入任何非TStrings数据时出现流错误的原因。

就像the earlier code需要将String数据和其他可变长度数据序列化为平面格式以了解一个字段在何处结束而下一个字段开始一样,您也需要对TStrings数据进行序列化也是

一个选择是先将TStrings对象保存到中间TMemoryStream,然后将该流的Size写入输出流,然后再写入TMemoryStream的数据。稍后再加载时,首先读取Size,然后将指定数量的字节读取到中间的TMemoryStream中,然后将该流加载到接收的TStrings对象中:

procedure WriteInt64ToStream(Stream: TStream; Value: Int64);
begin
  Stream.WriteBuffer(Value, Sizeof(Value));
end;

function ReadInt64FromStream(Stream: TStream): Int64;
begin
  Stream.ReadBuffer(Result, Sizeof(Result));
end;

procedure WriteStringsToStream(Stream: TStream; Values: TStrings);
var
  MS: TMemoryStream;
  Size: Int64;
begin
  MS := TMemoryStream.Create;
  try
    Values.SaveToStream(MS, TEncoding.UTF8);
    Size := MS.Size;
    WriteInt64ToStream(Stream, Size);
    if Size > 0 then
    begin
      MS.Position := 0;
      Stream.CopyFrom(MS, Size);
    end;
  finally
    MS.Free;
  end;
end;

procedure ReadStringsFromStream(Stream: TStream; Values: TStrings);
var
  MS: TMemoryStream;
  Size: Int64;
begin
  Size := ReadInt64FromStream(Stream);
  MS := TMemoryStream.Create;
  try
    if Size > 0 then
    begin
      MS.CopyFrom(Stream, Size);
      MS.Position := 0;
    end;
    Values.LoadFromStream(MS, TEncoding.UTF8);
  finally
    MS.Free;
  end;
end;

另一种选择是将TStrings对象中的字符串元素数写入输出流,然后编写各个字符串:

procedure WriteStringsToStream(Stream: TStream; Values: TStrings);
var
  Count, I: Integer;
begin
  Count := Values.Count;
  WriteIntegerToStream(Stream, Count);
  for I := 0 to Count-1 do
    WriteStringToStream(Stream, Values[I]);
end;

procedure ReadStringsFromStream(Stream: TStream; Values: TStrings);
var
  Count, I: Integer;
begin
  Count := ReadIntegerFromStream(Stream);
  if Count > 0 then
  begin
    Values.BeginUpdate;
    try
      for I := 0 to Count-1 do
        Values.Add(ReadStringFromStream(Stream));
    finally
      Values.EndUpdate;
    end;
  end;
end;

无论哪种方式,您都可以在流式传输单个记录时执行以下操作:

WriteIntegerToStream(SavingStream, Version);
WriteStringToStream(SavingStream, Name);
...
WriteStringsToStream(SavingStream, ColList1);
WriteStringsToStream(SavingStream, ColList2);
WriteBooleanToStream(SavingStream, ReadyToRun);

Version := ReadIntegerFromStream(SavingStream);
Name := ReadStringFromStream(SavingStream);
...
ReadStringsFromStream(SavingStream, ColList1);
ReadStringsFromStream(SavingStream, ColList2);
ReadyToRun := ReadBooleanFromStream(SavingStream);