TStringList 备份从旧版本到最新版本的兼容性

发布于 2024-12-14 01:39:06 字数 744 浏览 0 评论 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);
...

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

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

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

I have a backup system which uses a TStringList, but I code with an old Delphi (Ansi strings).

Basically I have this when I save:

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

And When I reload:

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

I use this sequential ( size + datasize bytes, then size + datasize bytes, etc) system for various component backup. In fact some stuffs are always 'read from' or 'written to' before the stringlist backup (I mean there are some data before and after the StringList backup).

Am I introducing a big problem here ( in case I switch to a modern Delphi version) ?
Will the chunk still be castable in future delphi version ( in case I switch ?). Would it be necessary for me to write the string version in the backup header ?

Unfortunately I cannot test this. I think that if I least I write the string encoding type in the header I'll be able to cast it in the right way later, whatever is the Delphi version, won't I?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

執念 2024-12-21 01:39:06

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

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

其次,从 D2009 开始,String 现在是 UnicodeString 而不是 AnsiStringPChar 现在是 PWideChar< /code> 而不是 PAnsiChar。您的 TStream 数据是 Ansi 而不是 Unicode(即使在 D2009+ 中也是如此,因为 SaveToStream() 默认使用 TEncoding.Default(即 Ansi)进行编码流数据),因此将数据转换为 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(#127 以上没有 AnsiChar 字符),则 UTF-8 与 ASCII 100% 向后兼容。但是,如果数据是 Ansi (在 #127 之上有 AnsiChar 字符),那么您最好以某种方式更改流格式(添加标头/版本等),以便您可以区分旧版本和较新的格式,然后您可以使用 Ansi 加载较旧的格式,并使用 Unicode/UTF-8 保存/加载较新的格式。

Use MyStringList.LoadFromStream(Str) instead of MyStringList.Text := PChar( Str.Memory).

First, your TStream data is not null-terminated, but using PChar the way you are requires a null terminator (you can use SetString() with a string variable to get around that).

Second, starting in D2009, String is now UnicodeString instead of AnsiString and PChar is now PWideChar instead of PAnsiChar. Your TStream data is Ansi instead of Unicode (even in D2009+ because SaveToStream() defaults to using TEncoding.Default, which is Ansi, for encoding the stream data), so casting the data to PWideChar will assign garbage to your TStringList.

In all versions, you should be using LoadFromStream(), but if you want to stick with setting the Text property then you need to do it like this, which works in all versions:

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;

Or this:

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;

Or this:

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;

Lastly, you should consider changing your stream data to use UTF-8 instead of Ansi for even better future compatibility. Both SaveToStream() and LoadFromStream() have an optional TEncoding parameter in D2009+, and UTF-8 is a lossless Unicode encoding whereas Ansi can loss data during Ansi/Unicode conversions. If your existing data is ASCII (no AnsiChar characters above #127), then UTF-8 is 100% backwards compatible with ASCII. But if the data is Ansi instead (has AnsiChar characters above #127), then you are best off changing your stream format in some way (add a header/version, etc) so you can differentiate between older and newer formats, then you can load older formats using Ansi, and save/load newer formats using Unicode/UTF-8.

冰雪之触 2024-12-21 01:39:06

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

I think you are on the right track. I remember, several years ago, I finished a similar task as you did. I had two sections for each chuck of data: header and content. Header contained information like starting address and length of the chunk. The content part contained actual data. This approach had never had any problem. In your case, the header only contained the size of the block. As to the version number of string, I recommend you to do so as, based on the release road of Delphi, it is very common that new releases are not backward compatible with old releases. Even if you don't have to use the version number later, it doesn't harm.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文