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的發佈道路,新版本與舊版本不兼容是很常見的。即使您以後不必使用版本號,也不會造成傷害。