Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 21.11.2013, 15:47
kti kti вне форума
Прохожий
 
Регистрация: 21.11.2013
Сообщения: 10
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию Сохранение record в файл

Подскажите, уважаемые, никак не получается, в делфи новичек...
В общем переделываю чужую программу.. там идет сохранение буфера в файл, вот такой кусок
Код:
var bufferTemplate1 : PChar;
    template1Length, pTransfered : integer;
    FS: TFileStream;
    pTemplate : pointer;
begin
     //Сохраняю шаблон в bufferTemplate1
      template1Length := Bsdk_Template_GetSize(pTemplate);
      GetMem(bufferTemplate1, template1Length);
      result := Bsdk_Template_Save(pTemplate, bufferTemplate1, template1Length, pTransfered);
      // после этой функции, в bufferTemplate1 содержаться данные

      //Сохраняю буфер bufferTemplate1 в файл Param1 + '.dat'
      FS := TFileStream.Create(Param1 + '.dat', fmCreate);
      FS.WriteBuffer(bufferTemplate1^, template1Length);
      FS.Free;
      FreeMem(bufferTemplate1);

данные из bufferTemplate1 записываются в файл. Мне надо чтобы к этим данным еще записывалась кое-какая информация, ну например имя "Вася", "Петя", и соответственно потом это дело загрузить в память обратно.
Делал через record, не получается:

Код:
 TTemplate = record
     Name: string[5];
     Template: PChar; // тут пробовал разные варианты, не помогает
   end;
var 
    tTempl : TTemplate;
    FS: TFileStream;
    pTemplate : pointer;
begin
     //Сохраняю шаблон в bufferTemplate1
      template1Length := Bsdk_Template_GetSize(pTemplate);
//   SetLength(tTempl.Template, '2696');
      GetMem(tTempl.Template, template1Length);
      result := Bsdk_Template_Save(pTemplate, tTempl.Template, template1Length, pTransfered);
      tTempl.name:= 'Вася';

      FS := TFileStream.Create('c:\temp\test.dat', fmCreate);
      FS.WriteBuffer(tTempl, SizeOf(TTemplate));
      FS.Free;
      FreeMem(tTempl.Template);
// записывается только 8-10 байт, хотя должно быть не менее 2696 байт
Ответить с цитированием
  #2  
Старый 21.11.2013, 16:21
Аватар для poli-smen
poli-smen poli-smen вне форума
Профессионал
 
Регистрация: 06.08.2012
Адрес: Кривой Рог
Сообщения: 1,791
Версия Delphi: Delphi 7, XE2
Репутация: 4415
По умолчанию

Примерно так:
Код:
var
.....
  len: Integer;
  s: AnsiString;
begin
.....

  // Сохраняем
  FS := TFileStream.Create(Param1 + '.dat', fmCreate);
  FS.WriteBuffer(template1Length, SizeOf(template1Length)); // Сначала записываем размер даных
  FS.WriteBuffer(bufferTemplate1^, template1Length); // А потом сами данные

  s := 'Вася';
  len := Length(s);
  FS.WriteBuffer(len, SizeOf(len)); // Сначала записываем длину строки
  FS.WriteBuffer(Pointer(s)^, len); // А потом саму строку
  FS.Free;
.....

  // Считываем
  FS := TFileStream.Create(Param1 + '.dat', fmOpenRead);
  FS.ReadBuffer(template1Length, SizeOf(template1Length)); // Сначала считываем размер даных
  GetMem(bufferTemplate1, template1Length); // Резервируем память под буфер требуемого размера
  FS.ReadBuffer(bufferTemplate1^, template1Length); // А потом считываем сами данные

  FS.ReadBuffer(len, SizeOf(len)); // Сначала считываем длину строки
  SetLength(s, len); // Резервируем память под строку требуемого размера
  FS.ReadBuffer(Pointer(s)^, len); // А потом считываем саму строку
  FS.Free;
Ответить с цитированием
Этот пользователь сказал Спасибо poli-smen за это полезное сообщение:
kti (21.11.2013)
  #3  
Старый 21.11.2013, 17:56
kti kti вне форума
Прохожий
 
Регистрация: 21.11.2013
Сообщения: 10
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Заработало! Спасибо огромное! т.е. по такому-же принципу я могу любые данные сохранить.. ясно
У меня еще пару вопросов если можно
1) Для загрузки данных в программе используется конструкция через TMemoryStream:

Код:
    TS:= TMemoryStream.Create;
    TS.LoadFromFile(Param1);

    templateLength:=TS.Size;
    GetMem(BufferTemplate, templateLength);
    TS.Position:=0;
    TS.ReadBuffer(BufferTemplate^, templateLength);

Чем этот подход принципиально может отличаться от Вашего (от TFileStream), почему был выбран именно TMemoryStream?

2) Мне нужно в этот-же файл дописывать данные. т.е например уже есть "Иванов", "Петров", мне нужно дописать в этот-же файл данные "Сидоров". Будет-ли работать такой вариант:

Код:
// файл уже существует, с 2 записями, мне надо добавить 3 запись
    FS := TFileStream.Create('c:\temp\test.dat', fmOpenRead);
// как-то надо спозиционировать FS на конец файла ??
    FS.WriteBuffer(template1Length, SizeOf(template1Length)); // Сначала записываем размер данных
    FS.WriteBuffer(bufferTemplate1^, template1Length); // А потом сами данные
Ответить с цитированием
  #4  
Старый 21.11.2013, 18:06
Аватар для poli-smen
poli-smen poli-smen вне форума
Профессионал
 
Регистрация: 06.08.2012
Адрес: Кривой Рог
Сообщения: 1,791
Версия Delphi: Delphi 7, XE2
Репутация: 4415
По умолчанию

Цитата:
Сообщение от kti
Заработало! Спасибо огромное! т.е. по такому-же принципу я могу любые данные сохранить.. ясно
У меня еще пару вопросов если можно
1) Для загрузки данных в программе используется конструкция через TMemoryStream:
.....
Чем этот подход принципиально может отличаться от Вашего (от TFileStream), почему был выбран именно TMemoryStream?
TMemoryStream - работает в памяти. TFileStream - работает с файлом.
Почему в той программе сделано через TMemoryStream я не знаю.

Цитата:
Сообщение от kti
2) Мне нужно в этот-же файл дописывать данные. т.е например уже есть "Иванов", "Петров", мне нужно дописать в этот-же файл данные "Сидоров". Будет-ли работать такой вариант:

Код:
// файл уже существует, с 2 записями, мне надо добавить 3 запись
    FS := TFileStream.Create('c:\temp\test.dat', fmOpenRead);
// как-то надо спозиционировать FS на конец файла ??
    FS.WriteBuffer(template1Length, SizeOf(template1Length)); // Сначала записываем размер данных
    FS.WriteBuffer(bufferTemplate1^, template1Length); // А потом сами данные
Просто пропускаем ненужные блоки:
Код:
FS.ReadBuffer(len, SizeOf(len)); // Считываем длину блока
FS.Position := FS.Position + len; // Пропускаем ненужный блок (вместо его чтения)
Ответить с цитированием
  #5  
Старый 21.11.2013, 18:41
kti kti вне форума
Прохожий
 
Регистрация: 21.11.2013
Сообщения: 10
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Просто пропускаем ненужные блоки:
Если я правильно понял - это при чтении, а как мне дописать в файл еще одну запись? В каком режиме создавать поток , fmOpenRead?
И как встать на конец файла перед записью нового потока?
FS.Position = EOF ? так? чтобы данные дописались к файлу

Код:
    FS := TFileStream.Create('c:\temp\test.dat', fmOpenRead); // ??
    FS.Position = EOF; // ???
// или нужно через FS.Position := FS.Position + len добираться до конца файла?
    FS.WriteBuffer(template1Length, SizeOf(template1Length)); // Сначала записываем размер данных
    FS.WriteBuffer(bufferTemplate1^, template1Length); // А потом сами данные
Ответить с цитированием
  #6  
Старый 21.11.2013, 19:02
kti kti вне форума
Прохожий
 
Регистрация: 21.11.2013
Сообщения: 10
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Все! разобрался сам. Достаточно было почитать про файловые потоки..

Код:
    FS := TFileStream.Create('c:\temp\test.dat', fmOpenReadWrite);
    FS.Seek(0, soFromEnd); // 
    FS.WriteBuffer(template1Length, SizeOf(template1Length)); // Сначала записываем размер данных
    FS.WriteBuffer(bufferTemplate1^, template1Length); // А потом сами данные

Остался один вопросик, что делает конструкция:

Код:
FS.create(FileName, fmCreate or fmOpenWrite)

т.е. если файл существует, то открывает для чтения/записи, а если нет то создает?
Если это так, то это то, что мне нужно
Ответить с цитированием
  #7  
Старый 23.11.2013, 09:23
kti kti вне форума
Прохожий
 
Регистрация: 21.11.2013
Сообщения: 10
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Еще вопросик
Как мне узнать (при чтении потока) что файл кончился?
И вообще как правильно обрабатывать ошибки чтения из потока?

Например, я читаю какой-то блок

Код:
FS.ReadBuffer(template1Length, SizeOf(template1Length)); // узнаем размер
GetMem(bufferTemplate1, template1Length); // выделяем память
FS.ReadBuffer(bufferTemplate1^, template1Length); // считываем данные

например размер блока 2000 байт, а реально в файле 1000 байт.
ReadBuffer вызовет исключение? как его обрабатывать?
Ответить с цитированием
  #8  
Старый 23.11.2013, 13:50
Аватар для poli-smen
poli-smen poli-smen вне форума
Профессионал
 
Регистрация: 06.08.2012
Адрес: Кривой Рог
Сообщения: 1,791
Версия Delphi: Delphi 7, XE2
Репутация: 4415
По умолчанию

Цитата:
Сообщение от kti
Все! разобрался сам. Достаточно было почитать про файловые потоки..
Ага. Справка великая сила
Хотя изучение исходников, наверное, ещё большая сила.

Цитата:
Сообщение от kti
Остался один вопросик, что делает конструкция:

Код:
FS.create(FileName, fmCreate or fmOpenWrite)

т.е. если файл существует, то открывает для чтения/записи, а если нет то создает?
Если это так, то это то, что мне нужно
Во-первых нужно создавать объект не от переменной, а от класса (т.е. TFileStream.Create, а не FS.Create).
Во-вторых в Delphi7 нельзя с флагом fmCreate использовать никакие другие флаги (в новых версиях Delphi вроде это поправили), но фактически эффект будет таким будто с флагом fmCreate был использован флаг fmOpenReadWrite.

Цитата:
Сообщение от kti
Еще вопросик
Как мне узнать (при чтении потока) что файл кончился?
И вообще как правильно обрабатывать ошибки чтения из потока?
.....
например размер блока 2000 байт, а реально в файле 1000 байт.
ReadBuffer вызовет исключение? как его обрабатывать?
Во-первых у потока есть свойства Size (общий размер потока) и Position (текущая позиция чтения/записи). Если значение Size совпадает с Position - значит мы в конце файла.
Во-вторых метод ReadBuffer используется как правило для гарантированного чтения указанного количества байт. Например мы читаем некоторый конкретный формат файла и судя по спецификации считаем что далее можем прочитать определённое количество байт, а оказывается что их прочиталось меньше, из-за чего ReadBuffer вызовет исключение, что для нас будет означать что либо неверный формат файла или файл повреждён (недокачан). Ну а исключения обрабатываются через конструкцию "try..except"
Если же нужно читать не гарантированное количество байт, а "сколько получится" - нужно вместо ReadBuffer использовать просто Read. Разница в том, что Read не вызовет исключения если прочиталось меньше байт чем указано и кроме того Read вернёт в результате сколько реально прочиталось байт, т.е. если мы пытались прочитать больше нуля байт, а Read вернула ноль, то значит мы уже в конце файла и читать из него больше нечего.
Ответить с цитированием
  #9  
Старый 25.11.2013, 15:53
kti kti вне форума
Прохожий
 
Регистрация: 21.11.2013
Сообщения: 10
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Во-первых нужно создавать объект не от переменной, а от класса (т.е. TFileStream.Create, а не FS.Create).

Да, это я неправильно написал. Решил проблему через следующую конструкцию:
Код:
  if FileExists(Param1 + Name_DB) then begin
        FS := TFileStream.Create(Param1 + Name_DB, fmOpenReadWrite);
      	FS.Seek(0, soFromEnd); // можно было FS.Position := FS.Size
    end
    else begin
        FS := TFileStream.Create(Param1 + Name_DB, fmCreate);
    end;

Цитата:
Если значение Size совпадает с Position - значит мы в конце файла.
здесь Position начинается с нуля? тогда наверно if TS.Position >= TS.Size-1 then ... файл кончился
Так?

Цитата:
а оказывается что их прочиталось меньше, из-за чего ReadBuffer вызовет исключение, что для нас будет означать что либо неверный формат файла или файл повреждён (недокачан).

вот я и хочу вставить проверку на битый файл, например кто-то взял и удалил последние пару байт

Цитата:
нужно вместо ReadBuffer использовать просто Read.

Понял, вот так и сделаю..
Еще раз спасибо
Ответить с цитированием
  #10  
Старый 25.11.2013, 16:25
Аватар для poli-smen
poli-smen poli-smen вне форума
Профессионал
 
Регистрация: 06.08.2012
Адрес: Кривой Рог
Сообщения: 1,791
Версия Delphi: Delphi 7, XE2
Репутация: 4415
По умолчанию

Цитата:
Сообщение от kti
Да, это я неправильно написал. Решил проблему через следующую конструкцию:
Код:
  if FileExists(Param1 + Name_DB) then begin
        FS := TFileStream.Create(Param1 + Name_DB, fmOpenReadWrite);
      	FS.Seek(0, soFromEnd); // можно было FS.Position := FS.Size
    end
    else begin
        FS := TFileStream.Create(Param1 + Name_DB, fmCreate);
    end;
Если делать через "Seek", тогда уже лучше не "Seek(0, soFromEnd)", а "Seek(0, soEnd)". А так особой разницы нет - свойство "Position" основано на методе "Seek".

Цитата:
Сообщение от kti
здесь Position начинается с нуля? тогда наверно if TS.Position >= TS.Size-1 then ... файл кончился
Так?
Position начинается с нуля. Например имеем файл размером 1000 байт, значит Position=0 будет соответствовать самому первому байту файла, а Position=999 будет соответствовать последнему байту файла, но при Position=999 ещё можно прочесть этот самый один последний байт и тогда станет Position=1000 (за концом файла), что означает что файл закончился. Т.е. проверка на конец файла должна быть такая:
Код:
if FS.Position = FS.Size then ShowMessage('Достигнут конец файла');
// или для надёжности можно проверять на "больше или равно":
if FS.Position >= FS.Size then ShowMessage('Достигнут конец файла');
Ответить с цитированием
  #11  
Старый 25.11.2013, 16:55
kti kti вне форума
Прохожий
 
Регистрация: 21.11.2013
Сообщения: 10
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от poli-smen
Position начинается с нуля. Например имеем файл размером 1000 байт, значит Position=0 будет соответствовать самому первому байту файла, а Position=999 будет соответствовать последнему байту файла, но при Position=999 ещё можно прочесть этот самый один последний байт и тогда станет Position=1000 (за концом файла), что означает что файл закончился. Т.е. проверка на конец файла должна быть такая:
Код:
if FS.Position = FS.Size then ShowMessage('Достигнут конец файла');
// или для надёжности можно проверять на "больше или равно":
if FS.Position >= FS.Size then ShowMessage('Достигнут конец файла');

Почему?! У меня данные считываются блоками в цикле:

Код:
     while 1=1 do begin
        // сам шаблон не считываем, а пропускаем
        if FS.Read(template1Length, SizeOf(template1Length)) <= 0 then break; // Сначала считываем размер данных
        FS.Position := FS.Position + template1Length; // Пропускаем ненужный блок (вместо его чтения)

        if FS.Position >= FS.Size-1 then break; // файл кончился
      end;

Если я буду сравнивать FS.Position >= FS.Size, то у меня начнется новый виток цикла (из-за этого 1000 байта), который мне совершенно не нужен.
Например, 1000 байт, 5 блоков по 200 байт. Сделав 5 витков - Position будет равен 999, и проверка FS.Position >= FS.Size не сработает, и начнется 6 виток. Или я что-то не понимаю..
Ответить с цитированием
  #12  
Старый 25.11.2013, 17:04
Аватар для poli-smen
poli-smen poli-smen вне форума
Профессионал
 
Регистрация: 06.08.2012
Адрес: Кривой Рог
Сообщения: 1,791
Версия Delphi: Delphi 7, XE2
Репутация: 4415
По умолчанию

Цитата:
Сообщение от kti
Если я буду сравнивать FS.Position >= FS.Size, то у меня начнется новый виток цикла (из-за этого 1000 байта), который мне совершенно не нужен.
Например, 1000 байт, 5 блоков по 200 байт. Сделав 5 витков - Position будет равен 999, и проверка FS.Position >= FS.Size не сработает, и начнется 6 виток. Или я что-то не понимаю..
Нет. Если первоначально было Position=0, то 5 раз прочитав по 200 байт станет Position=1000, а не 999. Чистая математика.
Ответить с цитированием
Этот пользователь сказал Спасибо poli-smen за это полезное сообщение:
kti (26.11.2013)
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 14:05.


 

Сайт

Форум

FAQ

Соглашения

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2025