Исходный текст архиватора Huffman'a (реализация на языке Object Pascal)


Содержание:

Часть первая

Часть вторая

Исходный текст архиватора (алгоритм Хаффмана)


unit Unit1;

interface


Uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls;
  (*
  Windows,//модуль поддерживает работу приложения с системой Win
  Messages,//модуль позволяет получать и обрабатывать сообщения Win(кнопка закрытия)
  SysUtils,//позволяет работать с фундаментальными типами данными (выделение памяти
  //под динамические переменные, работа с файл. перем., процедуры и
  //функции для работы со строковыми переменными
  Classes,//модуль в котором описываются некоторые классы Delphi (TStream)
  Graphics,//классы, процедуры и функции относящиеся к графике Win
  Controls,//здесь описаны классы TControl, TWinControl, TGraphicControl
  //прародители компонентов управления TButton, TLabel, TMemo, TEdit
  Forms,//модуль для создания и управления формами приложения
  Dialogs,//модуль для организации диалога с пользователем
  ExtCtrls,//содержит описание классов TImage, TBevel
  jpeg,//содержит средства для вывода картинок в формате JPG на форму
  StdCtrls,//содержит описание классов TEdit, TMemo
  MPlayer;//содержит средства для проигрывания видео в формате avi
  *)

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    OpenDialog1: TOpenDialog;
    ProgressBar1: TProgressBar;
    Label2: TLabel;
    Label3: TLabel;
    ProgressBar3: TProgressBar;
    Label4: TLabel;
    OpenDialog2: TOpenDialog;
    Button2: TButton;
    ProgressBar2: TProgressBar;
    Label1: TLabel;
    Memo2: TMemo;
    Label5: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
const
   count=4096;
//две файловые переменные для чтения исходного файла и для
//записи архива
var
   f_r,f_w: File;


implementation

{$R *.DFM}

Type
   //тип дерева для динамической обработки статистики байтов
   byte_=^byte_jachejka;
   byte_jachejka=Record
      //сам байт
      byte_id: Byte;
      //статистика байта
      byte_st: Integer;
      //последовательность битов, в которые преобразуется текущий
      //байт после сжатия (в виде строки из "0" и "1")
      byte_string: String;
      //ссылки на левое и правое поддеревья (ветки)
      left, right: Byte_;
   End;

   //массив со статистикой байтов, принимающих участие в
   //файле
   bytes_with_stat = Array [0..255] of byte_;

   //объект, инкапсулирующий в себе:
   // * массив байтов с их статистикой
   // * количество байт, содержащихся в файле (встречающихся в файле
   //   хотя бы один раз)
   // * процедура (метод) инициализации объекта
   // * процедура для увеличения частоты i-го байта
   // * метод чтения i-го байта и его статистики
   // * метод чтения массива байтов со статистикой
   TStat = Object
      massiv: bytes_with_stat;
      count_byte: Byte;
      Procedure create;
      Procedure inc(i: Byte);
      Function Read_(i: Byte): Byte_;
      Function Read_Bytes: bytes_with_stat;
   End;

   // * процедура (метод) инициализации объекта
   Procedure TStat.create;
   var
      i: Byte;
   Begin
      count_byte:=255;
      For i:=0 to count_byte do
      Begin
         new(massiv[i]);
         massiv[i]^.byte_id:=i;
         massiv[i]^.byte_st:=0;
         massiv[i]^.left:=nil;
         massiv[i]^.right:=nil;
         Application.ProcessMessages;
      End;
   End;

   // * процедура для увеличения частоты i-го байта
   Procedure TStat.inc(i: Byte);
   Begin
      massiv[i]^.byte_st:=massiv[i]^.byte_st+1;
   End;

   // * метод чтения i-го байта и его статистики
   Function TStat.Read_(i: Byte): Byte_;
   Begin
      Read_:=massiv[i];
   End;

   // * метод чтения массива байтов со статистикой
   Function TStat.Read_Bytes: bytes_with_stat;
   Begin
      Read_Bytes:=massiv;
   End;

Type
   //объект, инкапсулирующий в себе:
   // * имя и путь к архивируемому файлу
   // * размер архивируемого файла
   // * массив статистики частот байтов
   // * дерево частот байтов
   // * метод генерации по имени файла имени архива
   // * метод генерации по имени архива имени исходного файла
   File_=Object
      Name: String;
      Size: Integer;
      Stat: TStat;
      Derevo: Byte_;
      Function ArcName: String;
      Function FileSizeWOHead: Integer;
      Function DeArcName: String;
   End;

   // * метод генерации по имени архива имени исходного файла
   Function File_.DeArcName: String;
   Var
      i: Integer;
      name_: String;
   Begin
      name_:=name;
      if pos('.ups',name_)=0
      Then
      Begin
         ShowMessage('Неправильное имя архива');
         Application.Terminate;
      End
      Else
      Begin
         i:=Length(name_);
         While (i>0) And (name_[i]<>'!') Do
         Begin
            Dec(i);
            Application.ProcessMessages;
         End;
         If i=0
         Then
         Begin
            name_:=copy(name_,1,pos('.ups',name_)-1);
            If name_=''
            Then
            Begin
               ShowMessage('Неправильное имя архива');
               Application.Terminate;
            End
            Else
               DeArcName:=name_;
         End
         Else
         Begin
            name_[i]:='.';
            Delete(name_,pos('.ups',name_),4);
            DeArcName:=name_;
         End;
      End;
   End;

      Function File_.FileSizeWOHead: Integer;
      Begin
         FileSizeWOHead:=FileSize(f_r)
         -{размер исходного файла}4
         -{количество оригинальных байт}1
         -{количество байтов со статистикой}(Stat.count_byte+1)*5;
      End;

   // * метод генерации по имени файла имени архива
   Function File_.ArcName: String;
   Var
      i: Integer;
      name_: String;
   Const
      postfix='ups';
   Begin
      name_:=name;
      i:=Length(Name_);
      While (i>0) And not(Name_[i] in ['/','\','.']) Do
      Begin
         Dec(i);
         Application.ProcessMessages;
      End;

      If (i=0) or (Name_[i] in ['/','\'])
      Then
         ArcName:=Name_+'.'+postfix
      Else
         If Name_[i]='.'
         Then
         Begin
            //ArcName:=Copy(Name,1,i)+postfix;
            Name_[i]:='!';
            ArcName:=Name_+'.'+postfix;
         End;
   End;

   //процедура сортировки массива с байтами (сортировка производится
   //по убыванию частоты байта
   procedure sort_mass(var a: bytes_with_stat; length_mass: byte);
   var
      i,j: Byte;
      b: byte_;
   Begin
      if length_mass<>0
      Then
         for j:=0 to length_mass-1 do
         Begin
            for i:=0 to length_mass-1 do
            Begin
               If a[i]^.byte_st < a[i+1]^.byte_st
               Then
               Begin
                  b:=a[i]; a[i]:=a[i+1]; a[i+1]:=b;
               End;
               Application.ProcessMessages;
            End;
            Application.ProcessMessages;
         End;
   End;

   //процедура удаления динамической структуры частотного дерева
   //из памяти
   Procedure DeleteDr(head: Byte_);
   Begin
      Application.ProcessMessages;
      If head<>nil
      Then
      Begin
         DeleteDr(head^.left);
         DeleteDr(head^.right);
         Dispose(head);
         head:=nil;
      End;
   End;

   //создание дерева частот для архивируемого файла
   Procedure CreateDr(var head: byte_; massiv: bytes_with_stat;
                      last: byte);
   var
      b: byte_;
   Begin
      If last<>0
      Then
      Begin
         sort_mass(massiv, last);
         new(b);
         b^.byte_st:=massiv[last-1]^.byte_st + massiv[last]^.byte_st;
         b^.left:=massiv[last-1];
         b^.right:=massiv[last];
         massiv[last-1]:=b;
         /////////////////
         if last=1
         Then
         Begin
            head:=b;
         End
         Else
         Begin
            CreateDr(head,massiv,last-1);
         End;
      End
      Else
         Head:=massiv[last];
      Application.ProcessMessages;
   End;

var
   //экземпляр объекта для текущего сжимаемого файла
   main_file: file_;

//процедура для полного анализа частот байтов встречающихся хотя бы
//один раз в исходном файле
procedure stat_file(fname: String);
var
   f: file;
   i,j: Integer;
   buf: Array [1..count] of Byte;
   countbuf, lastbuf: Integer;
Begin
   AssignFile(f,fname);
   Try
      Reset(f,1);
      Main_file.Stat.create;
      Main_file.Size:=FileSize(f);
      ///////////////////////
      countbuf:=FileSize(f) div count;
      lastbuf:=FileSize(f) mod count;
      ////////////
      form1.ProgressBar1.Max:=countbuf;
      form1.ProgressBar1.Position:=0;
      ////////////
      For i:=1 to countbuf do
      Begin
         BlockRead(f,buf,count);
         for j:=1 to count do
         Begin
            Main_file.Stat.inc(buf[j]);
            Application.ProcessMessages;
         End;
         Application.ProcessMessages;
         form1.ProgressBar1.Position:=form1.ProgressBar1.Position+1;
      End;
      /////////////
      If lastbuf<>0
      Then
      Begin
         BlockRead(f,buf,lastbuf);
         for j:=1 to lastbuf do
         Begin
            Main_file.Stat.inc(buf[j]);
            Application.ProcessMessages;
         End;
         Application.ProcessMessages;
      End;
      CloseFile(f);
   Except
      ShowMessage('ошибка доступа к файлу!')
   End;
End;

//функция для определения по последовательности сжатых битов
//исходного несжатого байта
Function Found(derevo: byte_; i: byte): Boolean;
Begin
   Application.ProcessMessages;
   if (derevo=nil)
   Then
      Found:=False
   Else
   Begin
      if ((derevo^.left=nil) or (derevo^.right=nil))
      and (derevo^.byte_id=i)
      Then
         Found:=True
      Else
         Found:=Found(derevo^.left, i) or Found(derevo^.right, i);
   End;
End;

//функция для определения строкового представления сжатой последовательности
//битов для исходного байта i
Function Id_to_str_(derevo: byte_; i: Byte): String;
Begin
   Application.ProcessMessages;
   if derevo=nil
   Then
      id_to_str_:='+=='
   Else
   Begin
      if Found(derevo^.left,i)
      Then
         id_to_str_:='0'+id_to_str_(derevo^.left,i)
      Else
      Begin
         if Found(derevo^.right,i)
         Then
            id_to_str_:='1'+id_to_str_(derevo^.right,i)
         Else
         Begin
            If (derevo^.left=nil) and (derevo^.right=nil)
               and (derevo^.byte_id=i)
            Then
               id_to_str_:='+'
            Else
               id_to_str_:='';
         End;
      End;
   End;
End;

//вспомогательная функция для определения строкового представления
//сжатой последовательности битов для исходного байта i (с учетом
//того экстремального случая, когда исходный файл состоит всего из одного
//и того же байта)
Function Id_to_Str(derevo: byte_; i: Byte): String;
var
   s: String;
Begin
   s:=Id_To_Str_(derevo, i);
   If s='+'
   Then
      Id_to_Str:='0'
   Else
      Id_to_Str:=Copy(s,1,length(s)-1);
End;

//процедура записи сжатого потока битов в архив
Procedure Write_in_file(var buffer: String);
var
   i,j: Integer;
   k: Byte;
   s: String;
   a: Byte;
   buf: Array[1..2*count] of byte;
Begin
   i:=Length(buffer) div 8;
   //////////////////////////
   {Form1.ProgressBar2.Max:=i;
   Form1.ProgressBar2.Position:=0;}
   /////////////////////////
   For j:=1 to i do
   Begin
      buf[j]:=0;
      ///////////////////////////
      {Form1.ProgressBar2.Position:=Form1.ProgressBar2.Position+1;}
      For k:=1 to 8 do
      Begin
         If buffer[(j-1)*8+k]='1'
         Then
            buf[j]:=buf[j] or (1 shl (8-k));
         Application.ProcessMessages;
      End;
      Application.ProcessMessages;
   End;
   BlockWrite(f_w,buf,i);
   Delete(buffer,1,i*8);
End;

//процедура для окончательной записи остаточной цепочки битов в архив
Procedure Write_in_file_(var buffer: String);
var
   a,k: byte;
Begin
   Write_in_file(buffer);
   If length(buffer)>=8
   Then
      ShowMessage('ошибка в вычислении буфера')
   Else
      If Length(buffer)<>0
      Then
      Begin
         a:=255;
         for k:=1 to Length(buffer) do
         If buffer[k]='0'
         Then
            a:=a xor (1 shl (8-k));
         BlockWrite(f_w,a,1);
      End;
End;

Type
   Integer_=Array [1..4] of Byte;

//перевод целого числа в массив из четырех байт.
Procedure Int_to_Byte(i: Integer; var mass: Integer_);
var
   a: Integer;
   b: ^Integer_;
Begin
   b:=@a;
   a:=i;
   mass:=b^;
End;

//перевод массива из четырех байт в целое число.
Procedure Byte_to_Int(mass: Integer_; var i: Integer);
var
   a: ^Integer;
   b: Integer_;
Begin
   a:=@b;
   b:=mass;
   i:=a^;
End;

//процедура создания заголовка архива
Procedure Create_Head;
var
   b: Integer_;
   a: Integer;
   i: Byte;
Begin
   //Размер несжатого файла
   Int_To_Byte(main_file.Size,b);
   BlockWrite(f_w,b,4);
   //Количество оригинальных байт
   BlockWrite(f_w,main_file.Stat.count_byte,1);
   //Байты со статистикой
   For i:=0 to main_File.Stat.count_byte do
   Begin
      BlockWrite(f_w,main_File.Stat.massiv[i]^.byte_id,1);
      Int_To_Byte(main_file.Stat.massiv[i]^.byte_st,b);
      BlockWrite(f_w,b,4);
   End;
End;


const
   max_count=4096;
type
   buffer_=object
      b: Array [1..max_count] of Byte;
      bcount: Integer;
      count_global: Integer;
      Procedure Create_buf;
      Procedure Insert_byte(a: Byte);
      Procedure Flush_buf;
   End;
      /////////////////////////////
      Procedure buffer_.Create_buf;
      Begin
         bcount:=0;
         count_global:=0;
      End;
      ////////////////////////////////////////
      Procedure buffer_.Insert_byte(a: Byte);
      Begin
         if count_global0
         Then
            BlockWrite(f_w,b,bcount);
      End;

//создание деархивированного файла
Procedure Create_De_Arc;
var
   i,j: Integer;
   k: Byte;
   //////////////
   buf: Array [1..count] of Byte;
   countbuf, lastbuf: Integer;
   main_buffer: buffer_;
   tek_ukaz: Byte_;
Begin
   countbuf:=main_file.FileSizeWOHead div count;
   lastbuf:=main_file.FileSizeWOHead mod count;
   main_buffer.Create_buf;
   tek_ukaz:=main_file.Derevo;

   For i:=1 to countbuf do
   Begin
      BlockRead(f_r,buf,count);
      for j:=1 to count do
      Begin
         for k:=1 to 8 do
         Begin
            If (buf[j] and (1 shl (8-k)))<>0
            Then
               tek_ukaz:=tek_ukaz^.right
            Else
               tek_ukaz:=tek_ukaz^.left;

            if (tek_ukaz^.left=nil) or (tek_ukaz^.right=nil)
            Then
            Begin
               main_buffer.Insert_byte(tek_ukaz^.byte_id);
               tek_ukaz:=main_file.Derevo;
            End;

            Application.ProcessMessages;
         End;
         Application.ProcessMessages;
      End;
   End;
   If lastbuf<>0
   Then
   Begin
      BlockRead(f_r,buf,lastbuf);
      for j:=1 to lastbuf do
      Begin
         for k:=1 to 8 do
         Begin
            If (buf[j] and (1 shl (8-k)))<>0
            Then
               tek_ukaz:=tek_ukaz^.right
            Else
               tek_ukaz:=tek_ukaz^.left;

            if (tek_ukaz^.left=nil) or (tek_ukaz^.right=nil)
            Then
            Begin
               main_buffer.Insert_byte(tek_ukaz^.byte_id);
               tek_ukaz:=main_file.Derevo;
            End;

            Application.ProcessMessages;
         End;
         Application.ProcessMessages;
      End;
   End;
   main_buffer.Flush_buf;
End;

//процедура чтения заголовка архива
Procedure Read_Head;
var
   b: Integer_;
   byte_st: Integer;
   count_, byte_id, i, j: Byte;
Begin
   try
      //узнаем исходный размер файла
      BlockRead(f_r,b,4);
      Byte_to_Int(b,main_file.size);
      {}{}{}
      form1.label1.Caption:='size='+inttostr(main_file.size);
      {}{}{}
      //узнаем количество оригинальных байтов
      BlockRead(f_r,count_,1);
      {}{}{}
      form1.label1.Caption:=form1.label1.Caption+#13+'colbyte='+inttostr(count_);
      {}{}{}
      main_file.Stat.create;
      main_file.Stat.count_byte:=count_;
      //загоняем частоты в массив
      for i:=0 to main_file.Stat.count_byte do
      Begin
         BlockRead(f_r,byte_id,1);
         main_file.Stat.massiv[i]^.byte_id:=byte_id;
         BlockRead(f_r,b,4);
         Byte_to_Int(b,byte_st);
         main_file.Stat.massiv[i]^.byte_st:=byte_st;
         {}{}{}
          //form1.label1.Caption:=form1.label1.Caption+#13+'byte_id='+chr(byte_id)+#9+'byte_st='+inttostr(byte_st);
         {}{}{}
      End;
      CreateDr(main_file.Derevo,main_file.stat.massiv,main_file.stat.count_byte);
      /////////////
      /////////////
      /////////////
      /////////////
      form1.Memo2.text:=inttostr(main_file.Size)+#13#10;
      with main_file do
      for j:=0 to main_file.Stat.count_byte do
            form1.Memo2.Text:=form1.Memo2.Text+IntToStr(j)+#9+IntToStr(stat.massiv[j]^.byte_st)
            +#9+{Chr(stat.massiv[j]^.byte_id)+}'='+IntToStr(stat.massiv[j]^.byte_id)+#9+Id_to_str(derevo,stat.massiv[j]^.byte_id)+#13#10;
      /////////////
      /////////////
      form1.ProgressBar2.Position:=0;
      form1.ProgressBar2.Max:=main_file.size div 100;
      /////////////
      /////////////
      Create_De_Arc;
      //////////////
      DeleteDr(main_file.Derevo);
   except
      ShowMessage('архив испорчен!');
      //application.terminate;
   End;
End;

//процедура извлечения архива
Procedure Extract_File;
Begin
   AssignFile(f_r,main_file.Name);
   AssignFile(f_w,main_file.DeArcName+' new');
   try
      reset(f_r,1);
      rewrite(f_w,1);

      Form1.ProgressBar2.Position:=0;
      Form1.ProgressBar2.Max:=main_file.Size;

      //процедура чтения шапки файла
      read_head;

      closefile(f_r);
      closefile(f_w);
      Form1.label5.Caption:='extracting complete!';
      form1.Button1.Enabled:=True;
      form1.Button2.Enabled:=True;
   Except
      ShowMessage('Ошибка распаковки файла');
   End;
End;

//вспомогательная процедура для создания архива
Procedure Create_Arc;
var
   buffer: String;
   Array_St: Array [0..255] of String;
   i,j: Integer;
   //////////////
   buf: Array [1..count] of Byte;
   countbuf, lastbuf: Integer;
Begin
   Form1.Label4.Caption:='Wait';
   Application.ProcessMessages;
   AssignFile(f_r,main_file.Name);
   AssignFile(f_w,main_file.ArcName);
   Try
      Reset(f_r,1);
      Rewrite(f_w,1);
      For i:=0 to 255 Do Array_St[i]:='';
      For i:=0 to Main_file.Stat.count_byte do
      Begin
         Array_St[Main_file.Stat.massiv[i]^.byte_id]:=
         Main_file.Stat.massiv[i]^.byte_string;
         Application.ProcessMessages;
      End;
      countbuf:=main_file.Size div count;
      lastbuf:=main_file.Size mod count;
      Buffer:='';
      /////////////
      Form1.ProgressBar3.max:=countbuf;
      Form1.ProgressBar3.Position:=0;
      /////////////
      Create_Head;
      /////////////
      for i:=1 to countbuf do
      Begin
         BlockRead(f_r,buf,count);
         //////////////////////
         Form1.ProgressBar3.Position:=Form1.ProgressBar3.Position+1;
         For j:=1 to count do
         Begin
            buffer:=buffer+Array_St[buf[j]];
            If Length(buffer)>8*count
            Then
               Write_in_file(buffer);
            Application.ProcessMessages;
         End;
      End;
      If lastbuf<>0
      Then
      Begin
         BlockRead(f_r,buf,lastbuf);
         For j:=1 to lastbuf do
         Begin
            buffer:=buffer+Array_St[buf[j]];
            If Length(buffer)>8*count
            Then
               Write_in_file(buffer);
            Application.ProcessMessages;
         End;
      End;
      Write_in_file_(buffer);
      CloseFile(f_r);
      CloseFile(f_w);
      Form1.Label4.Caption:='O'' Kay!';
   Except
      ShowMessage('Ошибка создания архива');
   End;
End;

//главная процедура для создания архивного файла
Procedure Create_File;
var
   i, j: Byte;
   //mass: Bytes_with_stat;
Begin
   with form1 do
   Begin
      memo1.Text:='';
      With main_file do
      Begin
         {сортировка массива байтов с частотами}
         sort_mass(stat.massiv,stat.count_byte);
         {поиск числа задействованных байтов}
         i:=0;
         While (i0) do
         Begin
            inc(i);
         End;
         //////////////////////
         If Stat.massiv[i]^.byte_st=0
         Then
            i:=i-1;
         //////////////////////
         Stat.count_byte:=i;
         {создание дерева частот}
         CreateDR(Derevo,Stat.massiv,Stat.count_byte);
         {вывод на экран отчета}
         memo1.text:=inttostr(main_file.size)+#13#10;
         for j:=0 to i do
            Memo1.Text:=Memo1.Text+IntToStr(j)+#9+IntToStr(stat.massiv[j]^.byte_st)
            +#9+{Chr(stat.massiv[j]^.byte_id)+}'='+IntToStr(stat.massiv[j]^.byte_id)+#9+Id_to_str(derevo,stat.massiv[j]^.byte_id)+#13#10;
         {запоминаем результаты работы дерева}
         for i:=0 to Stat.count_byte do
            Stat.massiv[i]^.byte_string:=Id_to_str(Derevo,stat.massiv[i]^.byte_id);
         //пишем сам файл
         Create_Arc;
         //Удаляем уже ненужное дерево
         DeleteDr(Derevo);
         //Инициализируем статистику файла
         Main_file.Stat.create;
      End;
   End;
End;

procedure TForm1.Button1Click(Sender: TObject);
begin
   If OpenDialog1.Execute
   Then
   Begin
      main_file.Name:=OpenDialog1.FileName;
      main_file.Stat.create;
      stat_file(main_file.Name);
      create_file;
   End;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
   If OpenDialog2.Execute
   Then
   Begin
      Main_File.Name:=Form1.Opendialog2.FileName;
      label5.Caption:='';
      form1.Button1.Enabled:=false;
      form1.Button2.Enabled:=false;
      Extract_file;
   End;
end;

end.


Главная страница.

Hosted by uCoz