Пятница, 31.07.2026, 18:34
Учись хакать с малу!
Главная | Каталог статей | Регистрация | Вход
Меню сайта
Категории каталога
Мои статьи [8]
Мини-чат
500
Наш опрос
Оцените мой сайт
Всего ответов: 19
Главная » Статьи » Мои статьи

Архиватор на Delphi 7
Сегодня я расскажу вам, как пользоваться библиотекой ZLib которая начала поставляться с Delphi начиная с версии 7. Конечно, её можно было использовать и в предыдущих версиях, но для правильного функционирования программы нужно было всё время таскать вместе с программой библиотеку ZLib.dll. В седьмой версии всё стало проще, Delphi внедряла прямо в программу этот модуль с максимальной оптимизацией. Поэтому нам можно не думать о том, как программе носить с собой библиотеку ZLib.dll. 

  Итак, приступим к делу. 

Для того чтобы воспользоваться библиотекой ZLib нам надо подключить модуль ZLib к нашей программе. 
Этот модуль предоставляет нам два класса-потока: 
TCompressionStream 
и 
TDecompressionStream 

  Вот как будет выглядеть сжатие файла: 

var 

  source,dest:TFileStream;//поток-источноик, поток-приёмник 

  CompresSstream:TCompressionStream; // поток - архиватор 

  bytesread:integer;  

  mainbuffer:array[0..1023] of char; // буфер 

begin 

  source:=TFileStream.Create('< Путь к файлу >',fmOpenRead); // создаём поток - источник 

  dest:=TFileStream.Create('< Путь к результату >',fmCreate); // создаём поток - приёмник 

  CompresSstream:=TCompressionStream.Create(clMax,dest);// создаём поток - архиватор c // максимальной степенью сжатия 

  try // НА ВСЯКИЙ ПОЖАРНЫЙ 

  repeat  

  bytesread:=source.Read(mainbuffer,buffer_size); // считываем из источника в буфер 

  CompresSstream.Write(mainbuffer,bytesread); // записываем из буфера в поток - архиватор 

  until bytesread<1024; // всё это до тех пор пока весь источник не будет считан 

  except // если всё же возникнет какая-нибудь оБшибка!!! 

  CompresSstream.free; 

  source.Free; 

  dest.Free; 

  exit; 

  end; // всё прошло успешно 

  CompresSstream.free; 

  source.Free; 

  dest.Free; 

end; 


  Примерно так-же будет выглядеть распаковка файла 

var 

  source,dest:TFileStream; 

  decompressStream:TDecompressionStream; // 

  bytesread:integer; 

  mainbuffer:array[0..1023] of char; 

begin 

  source:=TFileStream.Create('< Путь к файлу >',fmOpenRead); 

  dest:=TFileStream.Create('< Путь к результату >',fmCreate); 

  decompressStream:=TDecompressionStream.Create(source); // 

  try 

  repeat 

  bytesread:=decompressStream.Read(mainbuffer,buffer_size); 

  dest.Write(mainbuffer,bytesread); 

  until bytesread<1024; 

  except 

  decompressStream.Free; 

  source.Free; 

  dest.Free; 

  exit; 

  end; 

  decompressStream.Free; 

  source.Free; 

  dest.Free; 

end; 


  НО не для этого я написал эту статью! Многие хотят, чтобы процесс архивации отображался на форме в каком-нибудь компоненте и чтобы форма не зависала во время архивации. Именно для этих целей я напишу модуль прямо на ваших глазах. В этом модуле тоже будет только две функции. 

Назавём этот модуль так: ZLibAdvTools. 

  ZLib 

  Advanced усовершенствованные 

  Tools инструменты 

  ZLibAdvTools. 

  ^^ ^ ^ 

  ZL A T 

Сначала про функцию сжатия. Назовём эту фунцкию ZLATCompressFile. 

function ZLATCompressFile( 

  sourcefile:string; //<----|---- это понятно 

  destinationfile:string; //<----| 

  compressRatio:integer;// степень сжатия 

  refreshWnd:boolean;// надо ли обновлять окно во время архивации 

  needcreate:boolean;// надо ли создавать файл-результат 

  progress:Pointer;//указатель на компонент прогресса, почему указатель, а не сам 

  //компонент объясню позже  

  min_value:integer; //<----|------- это тоже очень сильно пригодится 

  max_value:integer; //<---| 

  PositionPropName:string='Progress' // имя свойства прогресса 

  ):integer;// результат 

  

  Почему выбрал указатель на компонент прогресса, а не сам компонент. Для увеличения скорости работы функции. Ну, сами подумайте, если вы передадите своему другу на мыло 100 метровый файл или просто отправите на мыло его URL. Здесь та же система: зачем передавать объект, если можно просто указать, где он находится памяти. Теперь зачем нам надо min_value и max_value. Допустим надо отобразить процесс копирования и архивации на компоненте в 100 "делений", тогда копирование будет от 0 до 50, а архивация от 50 до 100, правильно? вот для чего нам нужны эти значения. Параметру PositionPropName уже присвоено значение по умолчанию, поэтому при вызове функции этот параметр нужно ставить, если только свойство, обозначающее положение прогресса имеет другое имя. 

  Теперь сама процедура: 

function ZLATcompressfile; 

var 

  source,dest:TFileStream;  

  CompresSstream:TCompressionStream;  

  bytesread:integer; 

  bytes_read_d:int64; // поясню потом 

  mainbuffer:array[0..buffer_size-1] of char; // константа нужна для универсальности 

  //её можно (т.е. нужно) объявить где-нибудь в начале 

  zena_byta:real; //<-----------------| 

  interval:integer; //<----------------|--------------терпение, только терпение!!!! всё объясню 

  needProgress:boolean; //<---------| 

begin 

  needProgress:=false;// обнуляю 

  source:=TFileStream.Create(sourcefile,fmOpenRead); // 

  if needcreate then 

  dest:=TFileStream.Create(destinationfile,fmCreate) // создаём объект вместе с созданием файла 

  else 

  dest:=TFileStream.Create(destinationfile,fmOpenWrite); // открываем в режиме чтения / записи 

  if compressRatio=0 then 

  CompresSstream:=TCompressionStream.Create(clDefault,dest);// степень сжатия по умолчанию 

  if compressRatio=1 then 

  CompresSstream:=TCompressionStream.Create(clFastest,dest);// самая быстрая степень сжатия 

  if compressRatio=2 then 

  CompresSstream:=TCompressionStream.Create(clMax,dest);// самая высокая степнь сжатия 

  if compressRatio>2 then 

  CompresSstream:=TCompressionStream.Create(clDefault,dest); 

  interval:=max_value-min_value; // узнаём интервал в " делениях " 

  if source.Size<>0 then 

  zena _ byta := interval / source . Size // zena _ byta = сколько байтов надо считать чтобы прибавилось  

  //одно "деление" 

  Else 

  zena_byta:=interval; // воизбежание оБшибок  

  Bytes_read_d:=0; // она будет хранить общее количество байт, которое мы считали: 

  // обнуляем её 

  if progress<>nil then needProgress:=true; //если указатель прогресс есть, то будем отображать 

  // архивацию . 

  if needprogress then 

  begin 

  { 

  

  Теперь настало время рассказать о супер-фишке которой нет и никогда не будет в С++ чисто по своему определению!!!! 

  procedure SetPropValue(Instance: TObject; const PropName: string; const Value: Variant); 

  Присваивает свойству PropName заданное как СТРОКА!!! у объекта Instance значение Value которое может, быть ЛЮБОГО типа (integer, real, string, array и т.д. и т.п.) 
  Эта процедура использует RTTI информацию объектов и очень сильно загромождает программу и советую её использовать, только если по другому нельзя!! 
  Кстати, тип Variant - это тоже ВЕЛИКОЕ достижение компании Borland. 
  И несколько слов про C++ Builder. По определению: откомпилированный экзешник языка С очень сильно отличается от Pascal'евского, поэтому С остаётся С даже если он от Borland} 

SetPropValue(TObject(progress^),'Progress',min_value); // устанавливаем прогресс в начало 

  end; 

  try 

  repeat // пошёл тот самый процесс 

  bytesread:=source.Read(mainbuffer,buffer_size); 

  CompresSstream.Write(mainbuffer,bytesread); 

  Bytes_read_d:=bytes_read_d+bytesread; // обновляем переменную 

  if refreshWnd then // если надо обновлять окно, то обновляем 

  Application.ProcessMessages; 

  if needprogress then // обновляем прогресс 

  begin  

  SetPropValue( 

  TObject (progress^), // получаем из указателя переменную и преобразуем её к PositionPropName ', // любому классу (какой тип не важно). 

  min_value+round(Bytes_read_d*zena_byta) //получаем текущее положение 

  ); 

  end; 

  until bytesread=0; 

  except // блин !!! провал 

  Result:=0; 

  CompresSstream.free; 

  source.Free; 

  dest.Free; 

  exit; 

  end; 

  CompresSstream.free; 

  if source.Size<>0 then 

  result:=round((100*dest.Size)/source.size) // результатом будет процент который составляет 

  // сжатый файл от исходного 

  else 

  result:=dest.Size; // опять провал (интересно, тогда что мы сжимали, если размер источника равен нулю) 

  source . Free ; 

  dest.Free; 

end; 


Таким образом Функция возвращает процент, а если оБшибка, то 0. 

С декомпрессией будет сложнее. 

  function ZLATDecompressfile( 

  sourcefile:string; 

  destinationfile:string; 

  refreshWnd:boolean; 

  needcreate:boolean; 

  progress:Pointer; 

  min_value:integer; 

  max_value:integer ; 

  PositionPropName:string='Progress' 

  ):boolean; 

  

  Здесь почти тоже самое только уже не нужна степень сжатия. Но есть другая проблема. Мы незнаем, сколько будет весить исходный файл, поэтому изначальное значение переменной zena_byta будет заведомо меньше. Например, мы распаковываем файл в 30 МВ, а изначально он весил 60 МВ, поэтому положение прогресса перейдёт за границу max_value. Как же решить эту проблему? Мы после каждой записи из буфера в переменную буде проверять, не превысил ли размер файла-приёмника размера сжатого файла, если да, то мы перерасчитаем переменную zena_byta. Конечно после таких действий при распаковке больших файлов и при слишком большом размере буфера прогресс будет скакать, то взад, то вперёд. Поэтому константе buffer_size лучше присвоить значение 1024 (это самое оптимальное значение - проверено опытом!!!). 

function ZLATdecompressfile; 

var 

  source,dest:TFileStream; 

  decompressStream:TDecompressionStream; 

  bytesread:integer; 

  bytesread_d:int64; 

  interval:integer; 

  zena_byta:real;  

  mainbuffer:array[0..buffer_size-1] of char; 

  needProgress:boolean;  

begin 

  needprogress:=false; 

  source:=TFileStream.Create(sourcefile,fmOpenRead); 

  if needcreate then 

  dest:=TFileStream.Create(destinationfile,fmCreate) 

  else 

  dest:=TFileStream.Create(destinationfile,fmOpenWrite); 

  decompressStream:=TDecompressionStream.Create(source); 

  result:=true; // результат - это успешность проделанной нами операции 

  interval:=max_value-min_value; 

  zena_byta:=interval/source.Size; 

  bytesread_d:=0; 

  if progress<>nil then needProgress:=true; 

  if needprogress then 

  begin 

  SetPropValue(TObject (progress^), PositionPropName,min_value); 

  end; 

  try 

  repeat 

  bytesread:=decompressStream.Read(mainbuffer,buffer_size); 

  dest.Write(mainbuffer,bytesread); 

  bytesread_d:=bytesread_d+bytesread; 

  if refreshWnd then 

  Application.ProcessMessages; 

  if needprogress then 

  begin  

  if bytesread_d>source.Size then // самое интересное !!! 

  begin // смотрим: если всего считано байт больше чем размер сжатого 

  zena_byta:=interval/bytesread_d; // то перерасчитываем переменную zena_byta 

  end; 

  SetPropValue(TObject(progress^), PositionPropName,min_value+round(bytesread_d*zena_byta)); 

  end; 

  until bytesread<buffer_size; 

  except //провал 

  result:=false; // результат = НЕТ 

  decompressStream.Free; 

  source.Free; 

  dest.Free; 

  exit; 

  end; 

  decompressStream.Free; 

  source.Free; 

  dest.Free; 

end; 

  

  Итак, теперь нам надо скопировать файл ZLibAdvTools.pas в папку Lib в корневой папке Delphi и подключить этот модуль в любом месте программы: Uses ZLibAdvTools; 

На сегодня пожалуй всё. 

Это моя первая статья, пожалуйста, оценивайте.

Категория: Мои статьи | Добавил: razhackay (13.11.2008)
Просмотров: 5156 | Комментарии: 1 | Рейтинг: 4.2/4 |
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа
Поиск
Друзья сайта
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Copyright MyCorp © 2026 Используются технологии uCoz