0

我有一个使用 TStringList 的备份系统,但我使用旧的 Delphi(Ansi 字符串)进行编码。

基本上我保存时有这个:

...
MyStringList.SaveToStream(Str);
StrSz := Str.Size;
MyBackupStream.Write(StrSz, SizeOf(Integer));
MyBackupStream.Write(Str.Memory^, StrSz);
...

当我重新加载时:

...
MyBackupStream.Read(StrSz, SizeOf(Integer));
Str.SetSize(StrSz);
MyBackupStream.Read(Str.Memory^, StrSz);
MyStringList.SetText := PChar( Str.Memory);
...

我使用这个顺序(大小 + 数据大小字节,然后大小 + 数据大小字节等)系统进行各种组件备份。事实上,有些东西总是在 stringlist 备份之前“读取”或“写入”(我的意思是在 StringList 备份之前和之后都有一些数据)。

我是否在这里引入了一个大问题(以防我切换到现代 Delphi 版本)?在未来的 delphi 版本中,该块是否仍可转换(以防我切换?)。我是否有必要在备份标头中写入字符串版本?

不幸的是,我无法对此进行测试。我认为,如果我至少在标头中编写字符串编码类型,那么以后我将能够以正确的方式进行转换,无论 Delphi 版本是什么,不是吗?

4

2 回答 2

2

使用MyStringList.LoadFromStream(Str)而不是MyStringList.Text := PChar( Str.Memory).

首先,您的TStream数据不是以空值结尾的,但是使用PChar您的方式需要一个空值终止符(您可以使用SetString()字符串变量来解决这个问题)。

其次,从 D2009 开始,String现在是UnicodeString代替,AnsiString现在PCharPWideChar代替PAnsiChar. 您的TStream数据是 Ansi 而不是 Unicode(即使在 D2009+ 中,因为SaveToStream()默认使用TEncoding.DefaultAnsi 来编码流数据),因此将数据转换为PWideChar将垃圾分配给您的TStringList.

在所有版本中,您都应该使用LoadFromStream(),但如果您想坚持设置Text属性,那么您需要这样做,这适用于所有版本:

var
  ...
  S: AnsiString;
begin
  ...
  MyBackupStream.ReadBuffer(StrSz, SizeOf(Integer));
  Str.SetSize(StrSz);
  if StrSz > 0 then MyBackupStream.ReadBuffer(Str.Memory^, StrSz);
  SetString(S, PAnsiChar(Str.Memory), StrSz);
  MyStringList.Text := String(S);
  ...
end;

或这个:

var
  ...
  S: AnsiString;
begin
  ...
  MyBackupStream.ReadBuffer(StrSz, SizeOf(Integer));
  if StrSz > 0 then begin
    SetLength(S, StrSz);
    MyBackupStream.ReadBuffer(S[1], StrSz);
  end;
  MyStringList.Text := String(S);
  ...
end;

或这个:

var
  ...
  Str: TStringStream;
begin
  ...
  Str := TStringStream.Create;
  try
    MyBackupStream.ReadBuffer(StrSz, SizeOf(Integer));
    if StrSz > 0 then Str.CopyFrom(MyBackupStream, StrSz);
    MyStringList.Text := Str.DataString;
  finally
    Str.Free;
  end;
  ...
end;

最后,您应该考虑将流数据更改为使用 UTF-8 而不是 Ansi,以获得更好的未来兼容性。两者在 D2009+SaveToStream()LoadFromStream()都有一个可选TEncoding参数,UTF-8 是无损 Unicode 编码,而 Ansi 在 Ansi/Unicode 转换过程中可能会丢失数据。如果您现有的数据是 ASCII(AnsiChar#127 以上没有字符),那么 UTF-8 与 ASCII 100% 向后兼容。但是,如果数据是 Ansi(具有AnsiChar#127 以上的字符),那么您最好以某种方式更改您的流格式(添加标题/版本等),以便您可以区分旧格式和新格式,然后您可以加载使用 Ansi 的旧格式,使用 Unicode/UTF-8 保存/加载新格式。

于 2011-11-06T00:23:13.833 回答
1

我认为你在正确的轨道上。我记得,几年前,我完成了和你类似的任务。对于每组数据,我有两个部分:标题和内容。标头包含块的起始地址和长度等信息。内容部分包含实际数据。这种方法从来没有任何问题。在您的情况下,标头仅包含块的大小。至于字符串的版本号,我建议你这样做,因为根据Delphi的发布路径,新版本不向后兼容旧版本是很常见的。即使您以后不必使用版本号,也没有坏处。

于 2011-11-05T23:27:09.943 回答