0

我有一个使用TStringList的备份系统,但是我用旧的Delphi(Ansi字符串)编码。从旧版本到最新版本的TStringList备份的兼容性

基本上我有此当我保存:

... 
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); 
... 

我用这个顺序(大小+命令datasize字节,则大小+命令datasize字节等)系统用于各种组件备份。事实上,在stringlist备份之前(我的意思是在StringList备份之前和之后有一些数据)之前,一些东西总是被'读取'或'写入'。

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

不幸的是我无法测试这个。我认为,如果我至少在头文件中编写字符串编码类型,那么以后我可以用正确的方式将其转换,无论是什么版本的Delphi,不是吗?

回答

2

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

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

其次,在D2009开始,String现在是UnicodeString,而不是AnsiStringPChar现在是PWideChar,而不是PAnsiChar。您的TStream数据是Ansi而不是Unicode(即使在D2009 +中,因为SaveToStream()默认为使用​​,即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来实现更好的未来兼容性。SaveToStream()LoadFromStream()在D2009 +中都有可选的TEncoding参数,而UTF-8是无损Unicode编码,而Ansi可以在Ansi/Unicode转换期间丢失数据。如果您现有的数据是ASCII(#127上面没有AnsiChar个字符),则UTF-8向后兼容ASCII 100%。但是,如果数据是Ansi(12732上方有AnsiChar个字符),那么您最好以某种方式更改流格式(添加标题/版本等),以便区分旧格式和新格式,然后您可以使用Ansi加载较旧的格式,并使用Unicode/UTF-8保存/加载较新的格式。

+0

当我使用loadfromstream我得到AV。我在之前的项目中注意到了这一点,对于我来说,使用TStringList.SaveToStream保存数据的唯一方法是将保存的数据作为字符串进行投射,如我的示例中所做的那样。我知道这很奇怪,但我不能用另一种方式来做... – az01

+0

AV意味着你正在访问无效的内存。 TStringList指针或TStream指针无效。正确使用时,LoadFromStream()工作得很好。我从来没有在任何版本中使用AV。如果你必须演员,那么你需要做的比你之前展示的更安全。你显示的内容不安全。它访问不属于TStream的周围内存。 –

+0

我已经将您的答案标记为已接受,但更多要关闭该主题。在我的备份流中,我有一个版本号。即使没有人在这里明确地告诉我,我将能够在最新版本的软件中投入旧数据,但我认为没关系。但是你把重点放在我重新加载字符串的方式上。对我来说,这是好事...我不期待这样的评论... – az01

1

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