Delphi - Сохранение записей в файл с использованием потоков - PullRequest
0 голосов
/ 04 января 2019

Delphi Tokyo - у меня есть файл параметров, который мне нужно сохранить (и позже загрузить) с диска.Параметры представляют собой серию объектов записи.Существует одна запись HEADER, а затем несколько записей COMMAND.Это настоящие записи (т.е. тип = записи).Запись HEADER содержит типы String, Boolean, Integer и TStringList.Я сохраняю, что, кажется, работает нормально, но когда я загружаю, что бы то ни было ПОСЛЕ TStringList вызывает ошибку чтения потока.Например ...

type tEDP_PROJ = record
   Version : Integer;
   Name: String;
    ...
   ColList1: TStringList;
   ColList2: TStringList;
   ReadyToRun : Boolean;
   ...
 end;

Когда я читаю ReadyToRun, я получаю ошибку чтения потока.Если я переместу его ДО TStringList (как в подпрограммах SAVE, так и в LOAD), то ReadyToRun загрузится правильно, но все, что будет после TStringList, вызовет ошибку.Интересно отметить, что ColList2 загружается нормально (хотя это не первый TStringList).

Я указываю метод кодирования при сохранении TStringList.

...
ColList1.SaveToStream(SavingStream, TEncoding.Unicode);
ColList2.SaveToStream(SavingStream, TEncoding.Unicode);

Я использую ту же кодировку при загрузке из потока (файла).

...
   ColList1.LoadFromStream(SavingStream, TEncoding.Unicode);
   ColList2.LoadFromStream(SavingStream, TEncoding.Unicode);

Обратите внимание, что когда я создаю StringList, я просто делаю стандартное создание ...

ColList1 := TStringList.Create;

Когда я сохраняю и загружаю, я следую примерам, которые Реми дал здесь ...

Кажется, что TStringList меняет способ, которым поток читает типы не-TStringList ... Что мне нужно сделать, чтобы это исправить?

1 Ответ

0 голосов
/ 04 января 2019

Почему вы используете TEncoding.Unicode?TEncoding.UTF8 имело бы больше смысла.

В любом случае, это не проблема кодирования.То, что вы пытаетесь сделать, просто не будет работать так, как вы пытаетесь это сделать, потому что TStrings данные имеют переменную длину и должны обрабатываться соответствующим образом.Тем не менее, TStrings не сохраняет какой-либо конечный разделитель или информацию о размере в выходной поток.При загрузке в поток TStrings.LoadFromStream() просто читает ВЕСЬ поток (в любом случае, все между текущим Position и концом потока).Вот почему вы получаете потоковые ошибки при попытке чтения / записи любых не TStrings данных после любых TStrings данных.

Так же, как более ранний код , необходимый для сериализации String данные и другие данные переменной длины в плоский формат, чтобы знать, где заканчивается одно поле и начинается следующее, необходимо также сериализовать данные TStrings.

Один из вариантов - сохранить объект TStringsсначала в промежуточный TMemoryStream, затем запишите этот поток Size в выходной поток, а затем данные TMemoryStream.При последующей загрузке сначала прочитайте Size, затем прочитайте указанное количество байтов в промежуточный TMemoryStream, а затем загрузите этот поток в ваш принимающий TStrings объект:

procedure WriteInt64ToStream(Stream: TStream; Value: Int64);
begin
  Stream.WriteBuffer(Value, Sizeof(Value));
end;

function ReadInt64FromStream(Stream: TStream): Int64;
begin
  Stream.ReadBuffer(Result, Sizeof(Result));
end;

procedure WriteStringsToStream(Stream: TStream; Values: TStrings);
var
  MS: TMemoryStream;
  Size: Int64;
begin
  MS := TMemoryStream.Create;
  try
    Values.SaveToStream(MS, TEncoding.UTF8);
    Size := MS.Size;
    WriteInt64ToStream(Stream, Size);
    if Size > 0 then
    begin
      MS.Position := 0;
      Stream.CopyFrom(MS, Size);
    end;
  finally
    MS.Free;
  end;
end;

procedure ReadStringsFromStream(Stream: TStream; Values: TStrings);
var
  MS: TMemoryStream;
  Size: Int64;
begin
  Size := ReadInt64FromStream(Stream);
  MS := TMemoryStream.Create;
  try
    if Size > 0 then
    begin
      MS.CopyFrom(Stream, Size);
      MS.Position := 0;
    end;
    Values.LoadFromStream(MS, TEncoding.UTF8);
  finally
    MS.Free;
  end;
end;

Другой вариантэто записать количество строковых элементов в объекте TStrings в ваш выходной поток, а затем записать отдельные строки:

procedure WriteStringsToStream(Stream: TStream; Values: TStrings);
var
  Count, I: Integer;
begin
  Count := Values.Count;
  WriteIntegerToStream(Stream, Count);
  for I := 0 to Count-1 do
    WriteStringToStream(Stream, Values[I]);
end;

procedure ReadStringsFromStream(Stream: TStream; Values: TStrings);
var
  Count, I: Integer;
begin
  Count := ReadIntegerFromStream(Stream);
  if Count > 0 then
  begin
    Values.BeginUpdate;
    try
      for I := 0 to Count-1 do
        Values.Add(ReadStringFromStream(Stream));
    finally
      Values.EndUpdate;
    end;
  end;
end;

В любом случае, вы можете сделать это при потоковой передаче ваших отдельных записей:

WriteIntegerToStream(SavingStream, Version);
WriteStringToStream(SavingStream, Name);
...
WriteStringsToStream(SavingStream, ColList1);
WriteStringsToStream(SavingStream, ColList2);
WriteBooleanToStream(SavingStream, ReadyToRun);

Version := ReadIntegerFromStream(SavingStream);
Name := ReadStringFromStream(SavingStream);
...
ReadStringsFromStream(SavingStream, ColList1);
ReadStringsFromStream(SavingStream, ColList2);
ReadyToRun := ReadBooleanFromStream(SavingStream);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...