【问题标题】:Delphi - Saving Records to File using StreamsDelphi - 使用 Streams 将记录保存到文件
【发布时间】:2019-01-03 22:41:39
【问题描述】:

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

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

当我阅读 ReadyToRun 时,我得到一个 Stream read 错误。如果我在 TStringList 之前移动它(在 SAVE 和 LOAD 例程上),那么 ReadyToRun 将正确加载,但在 TStringList 之后的任何内容都会导致错误。有趣的是,ColList2 加载良好(即使它不是第一个 TStringList)。

我在保存 TStringList 时指定了编码方法。

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

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

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

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

ColList1 := TStringList.Create;

当我保存和加载时,我按照 Remy 给出的示例 here...

TStringList 似乎正在改变流读取非 TStringList 类型的方式...我需要做什么来解决这个问题?

【问题讨论】:

标签: delphi stream


【解决方案1】:

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

无论如何,这不是编码问题。您尝试做的事情根本不会像您尝试做的那样工作,因为TStrings 数据是可变长度的,需要相应地处理。但是,TStrings 不会将任何类型的终止分隔符或大小信息保存到输出流中。在流中加载时,TStrings.LoadFromStream() 只是读取整个流(好吧,无论如何,当前 Position 和 End-Of-Stream 之间的所有内容)。这就是为什么在任何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);

【讨论】:

  • @@Remy - 我假设 TStringList.LoadFromStream 只会从流中加载自身然后停止,而不是期望“拥有”流的其余部分。无论如何,我使用了您的 MemoryStream 方法,并且效果很好。非常感谢!
  • @user1009073 SaveToFile() 调用SaveToStream(),生成的文件可以在文本编辑器中打开。所以SaveToStream() 以一种非常简单的格式保存,不允许LoadFrom(Stream/File)() 知道有多少个字符串属于它,所以它别无选择,只能加载流的其余部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
相关资源
最近更新 更多