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.
以上爲算法實現,最終寫完這篇博客居然用了一年;現在看以前的代碼發現有很多不成熟的地方;
我最終實現了將壓縮和解壓合併爲一個程序;源碼已經上傳,歡迎下載;