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类型的方式...我该怎么做才能解决此问题?
答案 0 :(得分:4)
您为什么要使用TEncoding.Unicode
? TEncoding.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);