Совместимость резервной копии TStringList от старых до новейших версий - PullRequest
0 голосов
/ 06 ноября 2011

У меня есть система резервного копирования, которая использует 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).

Я представляю здесь большую проблему (в случае, еслиЯ переключаюсь на современную версию Delphi)?Будет ли чанк по-прежнему кастоваться в будущей версии Delphi (на случай, если я переключусь?).Нужно ли мне писать строковую версию в резервном заголовке?

К сожалению, я не могу это проверить.Я думаю, что если я по крайней мере напишу строковый тип кодировки в заголовке, я смогу привести его правильным образом позже, какой бы ни была версия Delphi, не так ли?

Ответы [ 2 ]

2 голосов
/ 06 ноября 2011

Используйте MyStringList.LoadFromStream(Str) вместо MyStringList.Text := PChar( Str.Memory).

Во-первых, ваши TStream данные не заканчиваются нулем, но при использовании PChar в том виде, в каком вы есть, требуется нулевой терминатор (вы можете использовать SetString() со строковой переменной, чтобы обойти это).

Во-вторых, начиная с D2009, String теперь UnicodeString вместо AnsiString и PChar теперь PWideChar вместо 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 для еще лучшей совместимости в будущем. И SaveToStream(), и LoadFromStream() имеют дополнительный параметр TEncoding в D2009 +, а UTF-8 представляет собой кодировку Unicode без потерь, тогда как Ansi может потерять данные во время преобразования Ansi / Unicode. Если ваши существующие данные являются ASCII (без AnsiChar символов выше # 127), тогда UTF-8 на 100% обратно совместим с ASCII. Но если вместо этого используются данные Ansi (AnsiChar символов выше # 127), то лучше всего каким-то образом изменить формат вашего потока (добавить заголовок / версию и т. Д.), Чтобы вы могли различать старые и новые форматы, а затем вы можете загружать старые форматы с помощью Ansi и сохранять / загружать новые форматы с помощью Unicode / UTF-8.

1 голос
/ 06 ноября 2011

Я думаю, вы на правильном пути.Я помню, несколько лет назад я выполнил ту же задачу, что и вы.У меня было два раздела для каждого куска данных: заголовок и контент.Заголовок содержит информацию, такую ​​как начальный адрес и длина фрагмента.Содержательная часть содержала фактические данные.У такого подхода никогда не было проблем.В вашем случае заголовок содержал только размер блока.Что касается номера версии строки, я рекомендую вам сделать это, поскольку, исходя из пути выпуска Delphi, очень часто новые версии не имеют обратной совместимости со старыми версиями.Даже если вам не нужно использовать номер версии позже, это не навредит.

...