TStringList 备份从旧版本到最新版本的兼容性
我有一个使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
使用
MyStringList.LoadFromStream(Str)
而不是MyStringList.Text := PChar( Str.Memory)
。首先,您的
TStream
数据不是以 null 终止的,但是按照您的方式使用PChar
需要一个 null 终止符(您可以使用SetString()
使用字符串变量来解决这个问题)。其次,从 D2009 开始,
String
现在是UnicodeString
而不是AnsiString
,PChar
现在是PWideChar< /code> 而不是
PAnsiChar
。您的TStream
数据是 Ansi 而不是 Unicode(即使在 D2009+ 中也是如此,因为SaveToStream()
默认使用TEncoding.Default
(即 Ansi)进行编码流数据),因此将数据转换为PWideChar
会将垃圾分配给您的TStringList
。在所有版本中,您应该使用
LoadFromStream()
,但如果您想坚持设置Text
属性,那么您需要这样做,这适用于所有版本版本:或这样:
或这样:
最后,您应该考虑将流数据更改为使用 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 ofMyStringList.Text := PChar( Str.Memory)
.First, your
TStream
data is not null-terminated, but usingPChar
the way you are requires a null terminator (you can useSetString()
with a string variable to get around that).Second, starting in D2009,
String
is nowUnicodeString
instead ofAnsiString
andPChar
is nowPWideChar
instead ofPAnsiChar
. YourTStream
data is Ansi instead of Unicode (even in D2009+ becauseSaveToStream()
defaults to usingTEncoding.Default
, which is Ansi, for encoding the stream data), so casting the data toPWideChar
will assign garbage to yourTStringList
.In all versions, you should be using
LoadFromStream()
, but if you want to stick with setting theText
property then you need to do it like this, which works in all versions:Or this:
Or this:
Lastly, you should consider changing your stream data to use UTF-8 instead of Ansi for even better future compatibility. Both
SaveToStream()
andLoadFromStream()
have an optionalTEncoding
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 (noAnsiChar
characters above #127), then UTF-8 is 100% backwards compatible with ASCII. But if the data is Ansi instead (hasAnsiChar
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.我认为你走在正确的道路上。我记得,几年前,我完成了和你类似的任务。我为每个数据块设置了两个部分:标题和内容。标头包含块的起始地址和长度等信息。内容部分包含实际数据。这种方法从来没有出现过任何问题。在您的情况下,标头仅包含块的大小。至于字符串的版本号,我建议你这样做,因为根据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.