利用huffman樹實現字符文檔的壓縮

huffman樹壓縮字符文檔,就是將8bit的字符根據出現頻率進行重新編碼,使編碼之後每個字符的編碼在文件讀入時都能被唯一確定。故字符的編碼必須是不頭包含的。huffman樹是一種簡單的壓縮編碼方式。

本文將採用c語言實現。編譯環境爲gcc4.9.2,故可以採用c++的引用傳遞使代碼更加簡單。


首先,先編一個頭文件“huffman.h”實現實現huffman樹的基本操作,代碼:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>


typedef struct{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;         //動態分配數組存儲赫夫曼樹 
typedef char * *HuffmanCode;   //動態分配數組存儲赫夫曼編碼表 


void Select(HuffmanTree HT,int n,int &s1,int &s2)
{int i,te;
 int p1,p2;
 for(i=1;i<=n;i++)
  {if(HT[i].parent==0)
  {
  p1=i;
  i++;
  break;
}
  }
 for( ;i<=n;i++)
  {if(HT[i].parent==0)
  {
  p2=i;
  i++;
  break;
}
  }
  if(HT[p1].weight>HT[p2].weight)
  {
  te=p1;
  p1=p2;
  p2=te;
}  //p1,p2賦初始值 
  for( ;i<=n;i++)
  {if(HT[i].parent==0)
  {
  if(HT[i].weight<HT[p1].weight)
  {
  p2=p1;
p1=i;
}
else
if(HT[i].weight<HT[p2].weight)
p2=i;
}
  } 
 s1=p1;
 s2=p2; 
}


void HuffmanCoding (HuffmanTree &HT,HuffmanCode &HC ,int *w,int n){
//w存放n個字符的權值(均>0),構造赫夫曼樹HT,並求出n個字符的赫夫曼編碼HC
//HT,HC空間在函數中分配,數組w,總數n 
HuffmanTree p=NULL;
int m,i;
int s1,s2;
char *cd;
int start,c;
int f;//分配變量 
if(n<=1)return ;
m=2*n-1;//i m s1 s2 HT[0] HT[1] HT[2] HT[3] HT[4] HT[5] HT[6]
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0號單元未用
for(i=1;i<=n;i++) 
HT[i]={w[i],0,0,0};
for( ;i<=m;++i){//赫夫曼樹 
//在HT[1..i-1]選擇parent爲0且weight最小的兩個節點,其序號分別爲s1和s2
Select(HT,i-1,s1,s2);
HT[s1].parent=i;HT[s2].parent=i;
HT[i].lchild=s1;HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight; 
HT[i].parent=0; 

//---從葉子到根逆向求每個字符的赫夫曼編碼--- 

HC=(HuffmanCode)malloc((n+1)*sizeof(char *));// 分配n個字符編碼的頭指針向量 
cd=(char *)malloc(n*sizeof(char));//分配求碼的工作空間 
cd[n-1]='\0'; //編碼結束符


for(i=1;i<=n;++i){//逐個字符求赫夫曼編碼 
  start = n-1;//編碼結束符位置
for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//從葉子到根逆向求編碼
if(HT[f].lchild==c) cd[--start]='0';
else cd[--start]='1';
HC[i]=(char *)malloc((n-start)*sizeof(char));//爲第i個字符編碼分配空間
strcpy(HC[i],&cd[start]);//從cd複製編碼(串)到HC 
}
free(cd); //釋放工作空間 
}//HuffmanCoding

壓縮文件的程序“壓縮.cpp”,代碼:

#include<stdio.h>
#include<string.h> 
#include<stdlib.h>
#include"huffman.h"


int main()
{HuffmanTree HT;
 HuffmanCode HC;
 char te;
 int w[257];//存放權值 
 int i; 
 unsigned int a;
 for(a=0;a<=128;a++) w[a]=0;
 FILE * f1;
 FILE * f2; 
 char name[1004],name1[1004];
 scanf("%s",name);
 if((f1=fopen(name,"r"))==NULL){
  printf("can't open this file.\n");
  exit(0);
 }


 while(te=fgetc(f1)){
  if(te==-1)break;
  a=te;
  w[a+1]++;
 }                       //第一次讀入計算權值 
 fseek(f1,0L,SEEK_SET);
 
 HuffmanCoding(HT,HC,w,128);
 
 for(i=1;i<=128;i++)
  printf("%d %s\n",i-1,HC[i]);
    printf("\n\n\n");            //輸出Huffman字符和編碼 
 strcpy(name1,name);
 strcat(name1,".out");   
 if((f2=fopen(name1,"wb+"))==NULL){
  printf("can't open this file.\n");
  exit(0);
 }
 for(i=1;i<=128;i++)
  fwrite(&w[i],4,1,f2);
  
 int da[9];
 int j=1;
 unsigned char c1=0;
 while(te=fgetc(f1)){
  if(te==-1)break;
  for(i=0;i<strlen(HC[te+1]);i++)
  {if(j<9)
    if(HC[te+1][i]=='1'){da[j]=1;j++;}
    else {da[j]=0;j++;}
  if(j==9)
   {c1=da[1]*128+da[2]*64+da[3]*32+da[4]*16+da[5]*8+da[6]*4+da[7]*2+da[8];
  //fwrite(&c1,1,1,f2);
  a=0;
  j=1;
 }
    }
 }//粗略壓縮 
 
 printf("%d\n",j-1);
 for(i=1;i<j;i++)
printf("%d\n",da[i]);
 j--;
 fwrite(&j,4,1,f2);
 for(i=1;i<=j;i++)
{fwrite(&da[i],4,1,f2);
  printf("%d\n",da[i]);
}


  j=1;
 fseek(f1,0,SEEK_SET);
  while(te=fgetc(f1)){
  //printf("%c",te);
  if(te==-1)break;
  for(i=0;i<strlen(HC[te+1]);i++)
  {if(j<9)
    if(HC[te+1][i]=='1'){da[j]=1;j++;}
    else {da[j]=0;j++;}
   if(j==9)
    {c1=da[1]*128+da[2]*64+da[3]*32+da[4]*16+da[5]*8+da[6]*4+da[7]*2+da[8];
   fwrite(&c1,1,1,f2);
   a=0;
   j=1;
  }
     }
    }//粗略壓縮 
 fclose(f1); 
 fclose(f2);
 return 0;
}



之後是解壓

#include<stdio.h>
#include<stdlib.h>
#include"huffman.h"


int main()
{FILE * f1;
 FILE * f2;
 bool da[9];
 HuffmanTree HT;
 HuffmanCode HC;
 char name[1004],name1[1004];
 scanf("%s",name);
 strcpy(name1,name);
 strcat(name1,".out");   
 f1=fopen(name1,"rb");
 int w[129];
 int i,j;
 for(i=1;i<=128;i++)
  {fread(&w[i],4,1,f1);
   printf("%d ",w[i]);
  }
 printf("\n"); 
 fread(&j,4,1,f1);
 printf("%d ",j);
 
 for(i=1;i<=j;i++)
{fread(&da[i],4,1,f1);
printf("%d ",da[i]);
}
 
 HuffmanCoding(HT,HC,w,128);
 for(i=1;i<=128;i++)
 {printf("%s\n",HC[i]);
 }
 
 //fclose(f1);
 f2=fopen(name,"w");
 //strcpy(name1,name);
 //strcat(name1,".out");   
 // f2=fopen(name1,"rb");
 int buf;
 int a;
 int *bu;
 bu=&buf;
 int end=0;
 end=255;//вС0ср1 
 while(fread(bu,1,1,f1))
 {a=buf/128;
  buf=buf-a*128;
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1); 
   fprintf(f2,"%c",end-1);
   end=255;
  } 
  a=buf/64;
  buf=buf-a*64;
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1); 
   fprintf(f2,"%c",end-1);
   end=255;
  } 
  a=buf/32;
  buf=buf-a*32;
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1);
   fprintf(f2,"%c",end-1); 
   end=255;
  }
  a=buf/16;
  buf=buf-a*16;
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1);
   fprintf(f2,"%c",end-1); 
   end=255;
  }
  a=buf/8;
  buf=buf-a*8;
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1);
   fprintf(f2,"%c",end-1); 
   end=255;
  }
  a=buf/4;
  buf=buf-a*4;
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1);
   fprintf(f2,"%c",end-1); 
   end=255;
  }
  a=buf/2;
  buf=buf-a*2;
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1);
   fprintf(f2,"%c",end-1); 
   end=255;
  }     
  a=buf;
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1); 
   fprintf(f2,"%c",end-1);
   end=255;
  }
 } 
for(i=1;i<=j;i++)
 {a=da[i];
  if(a==0) end=HT[end].lchild;
  else end=HT[end].rchild; 
  if(end<=128)
  {//printf("%c",end-1); 
   fprintf(f2,"%c",end-1);
   end=255;
 }
}
 
  



P.S.

以上爲算法實現,最終寫完這篇博客居然用了一年;現在看以前的代碼發現有很多不成熟的地方;

我最終實現了將壓縮和解壓合併爲一個程序;源碼已經上傳,歡迎下載;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章