Чтение и запись в двоичный файл?

Я сохраняю некоторые специально созданные классы объектов (потоковые данные) в файл.

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

Это не работает, потому что LoadFromFile не может правильно проанализировать файл. Я предполагаю, что из-за забавных символов поток сохраняет в файл как, а TStringList ожидает обычную текстовую информацию.

Как я могу сделать следующее:

  • Прочитайте любой необработанный двоичный файл в TStringList.
  • Добавьте мою новую строку, например StringList1.Add(MyString);
  • Сохраните необработанный двоичный файл еще раз.

Этот вопрос на самом деле относится к другому вопросу, который я задал: Сохранить значение CRC в файле без изменения фактической контрольной суммы CRC?

Вот что я пытаюсь сделать:

  • Вычислите контрольную сумму CRC моего файла сохраненного потока.
  • Добавьте значение CRC в конец файла.
  • Пересохраните файл.

Затем, когда я пытаюсь открыть свой потоковый файл:

  • Присвойте значение CRC (в конце файла) переменной.
  • Удалите значение CRC из файла.
  • Сохраните потоковый файл как новый временный файл.
  • Вычислите и сравните CRC временного файла с CRC, сохраненным в переменной.
  • Если CRC файла соответствует внутреннему сохраненному значению CRC, я могу обработать файл как обычно.

Но я не знаю, как читать или записывать необработанные двоичные данные файла.

Буду признателен, если кто-то сможет мне помочь и посоветовать:)


person Community    schedule 23.12.2011    source источник
comment
Вы сохраняете контрольную сумму в текстовый файл? Это правильно? Я до сих пор не понимаю, зачем вам контрольная сумма.   -  person David Heffernan    schedule 23.12.2011
comment
Сохраняя в потоковом файле, я делаю это для того, чтобы всякий раз, когда файл открывается из моего приложения, я мог определить, действительно ли файл был первоначально сохранен из моей программы.   -  person    schedule 23.12.2011
comment
Вы хотите, чтобы файл был доступен для чтения в текстовом редакторе? FWIW, ваш текущий подход чрезвычайно расточительный с ложным чтением и записью.   -  person David Heffernan    schedule 23.12.2011
comment
Какую версию Делфи вы используете? Это имеет значение, в частности, для строк Unicode (или нет)   -  person David Heffernan    schedule 23.12.2011
comment
Это не имеет особого смысла. Возможно, вам действительно следует указать больше того, что вы на самом деле собираетесь делать с этими двоичными файлами. Лично я не думаю, что вам следует использовать TStringList для хранения двоичных данных. Как вы собираетесь решить, что такое новая строка (возврат каретки + эквивалент перевода строки? Или вы собираетесь загружать один блок данных (скажем, 1024 байта) для каждого элемента в BinaryStringList?   -  person Warren P    schedule 23.12.2011
comment
Я использую DelphiXE. Файл сохраняется как поток и поэтому не читается, и это нормально. Мне не нужно редактировать файл вне моей программы, как раз наоборот. Мне просто нужно добавить строку, содержащую CRC файла, чтобы закончить, но это не работает, потому что я не могу прочитать файл. Мне кажется, я опять все путаю и из простого делаю сложную ситуацию.   -  person    schedule 23.12.2011
comment
В двоичном файле нет строки, поэтому добавить ее нельзя.   -  person Uwe Raabe    schedule 23.12.2011
comment
Мое мнение, что вы без нужды усложняете себе жизнь. Я не могу себе представить, почему вы хотите затруднить чтение и запись файлов. Пользователю сложно автоматизировать приложение. И рискованно, если вам нужно изменить форматы файлов. Если вам нужно сделать что-то подобное, поместите это в другой файл с базовым именем, которое соответствует текстовому файлу. Вы слишком усложняете и пытаетесь решить проблемы, которых не существует.   -  person David Heffernan    schedule 23.12.2011
comment
Если я попытаюсь открыть файл, который был сохранен из моей программы, а файл поврежден (например, поток в файле был прерван), программа все равно попытается открыть файл потока, даже если она не может, она может сказать Недостаточно памяти ошибка например, а иногда вообще ничего. Блок Try..Except, похоже, не понимает, что файл не может быть открыт, и не вызывает исключения. Поэтому я подумал, что могу включить значение контрольной суммы CRC файла внутри и таким образом проверить целостность файла. Но да, кажется, я усложняю себе задачу.   -  person    schedule 23.12.2011
comment
Было бы гораздо лучше обнаружить поврежденный файл и сообщить об ошибке. Если пользователь портит файл, то это его проблема. Храните его под своим профилем пользователя, и у них нет причин возиться с ним.   -  person David Heffernan    schedule 23.12.2011
comment
Это не тот файл, который я могу просто спрятать во временном каталоге, так как его можно загрузить из TOpenDialog. Я согласен, если кто-то решит испортить файл по своей вине, но по какой-то причине я не могу отловить ошибку, если файл (потоковые данные) поврежден - он почти всегда не показывает ошибку, но все равно пытается ее обработать.   -  person    schedule 23.12.2011
comment
Я думаю, что решил это, и на этот раз, не создавая больше проблем, чем нужно! Я пытался поймать исключение в своей процедуре, которая открывает файл, вместо этого я создал блок try..exception в фактическом базовом классе, где я загружаю поток, если есть исключение, я устанавливаю логический флаг, чтобы сообщить мне, если файл загрузился нормально. Я проверяю этот флаг в процедуре Open и могу определить, в порядке ли файл или нет. вроде так работает. Я не понимаю, почему я путаюсь и усложняю задачи :(   -  person    schedule 23.12.2011
comment
Крейг; Бесплатный наконечник; Двоичные файлы - это проблема. Если вам не нужно создавать пользовательские форматы двоичных файлов, не делайте этого. Текстовые файлы гораздо более прочные и надежные, и их легче устранять, если они искажены.   -  person Warren P    schedule 23.12.2011
comment
Уоррен, спасибо за подсказку. Я могу подумать об изменении способа сохранения своих объектов в файл, возможно, XML будет лучшим выбором.   -  person    schedule 24.12.2011
comment
@Craig XML был бы хорошим выбором. JSON гораздо более удобочитаем, если вы когда-нибудь захотите, чтобы его анализировал человек. Лично мне больше всего нравится YAML, но это требует больших усилий, чтобы попасть в Delphi. Либо JSON, либо XML — хороший выбор.   -  person David Heffernan    schedule 24.12.2011
comment
@David Дэвид, я ничего не знаю о формате файлов JSON или YAML, не читая об этом. Я читал еще немного о XML, и я вижу, что это не должно быть слишком большой проблемой для реализации. Мне не очень нравится, как потоки сохраняются в файл, и, как вы говорите, по крайней мере что-то вроде XML позволит при желании читать.   -  person    schedule 25.12.2011
comment
Какой синтаксический анализатор XML вы планируете использовать?   -  person David Heffernan    schedule 25.12.2011
comment
Я думал об использовании TXMLDocument и сохранении своих объектов в виде узлов XML.   -  person    schedule 26.12.2011


Ответы (1)


Этот класс происходит от TStringList и добавляет контрольное значение в конце при записи в файл. Это значение проверяется всякий раз, когда файл читается.

type
  TCRCStringList = class(TStringList)
  type
    TCRC = LongWord;
  private
    function CalcCRC(Stream: TStream): TCRC;
  public
    procedure LoadFromStream(Stream: TStream; Encoding: TEncoding); override;
    procedure SaveToStream(Stream: TStream; Encoding: TEncoding); override;
  end;

function TCRCStringList.CalcCRC(Stream: TStream): TCRC;
begin
  Result := 42;  // place CRC calculation here
end;

procedure TCRCStringList.LoadFromStream(Stream: TStream; Encoding: TEncoding);
var
  crc: TCRC;
  temp: TMemoryStream;
begin
  temp := TMemoryStream.Create;
  try
    temp.CopyFrom(Stream, Stream.Size - Sizeof(crc));
    Stream.Read(crc, Sizeof(crc));
    if crc <> CalcCRC(temp) then
      raise Exception.Create('CRC error');
    temp.Position := 0;
    inherited LoadFromStream(temp, Encoding);
  finally
    temp.Free;
  end;
end;

procedure TCRCStringList.SaveToStream(Stream: TStream; Encoding: TEncoding);
var
  crc: TCRC;
  temp: TMemoryStream;
begin
  temp := TMemoryStream.Create;
  try
    inherited SaveToStream(temp, Encoding);
    temp.Position := 0;
    crc := CalcCRC(temp);
    temp.Position := temp.Size;
    temp.Write(crc, Sizeof(crc));
    Stream.CopyFrom(temp, 0); // count = 0 copies the whole stream from the beginning
  finally
    temp.Free;
  end;
end;
person Uwe Raabe    schedule 23.12.2011
comment
спасибо за публикацию, ваш код выглядит интересно. С тех пор я решил свою первоначальную проблему без использования какой-либо проверки CRC. Однако я приму ваш ответ, поскольку он кажется очень полезным, мне придется рассмотреть его более внимательно, чтобы изучить его. Спасибо - person ; 23.12.2011