一个编程小白成功的完成了哈夫曼树的文件压缩,过程可以说是很艰辛了。好啦~废话就不多说了,下面开始正式的讲解,过程+代码。
工具:Dev-c++
语言:C++
刚开始我拿到这个题目,真心是蒙啊,因为我连什么是哈夫曼都不知道(可以看出我是小白了吧)。
先说一下整个过程:
1.从文件中读取叶子个数,及权值(即相同字母的个数)。比如说文件中内容是abcdabcaba,叶子个数为4,分别是a,b,c,d,权值依次为4,3,2,1.
2.构造哈夫曼树
3.得到哈夫曼编码
4.压缩
(1)将Y,S写入要压缩的文件中作为标记位。(主要是为了在解压缩过程中判断是否为压缩文件)
(2)将叶子个数写入压缩文件
(3)将所有叶子按照叶子所对应字符,编码长度(即01的个数 ),转换后的编码长度(即二进制转换成十六进制后所得编码长度),转换后的编码,这样的顺序依次写到压缩文件中
(4)写入八位对齐补偿长度
(5)写入原文件哈夫曼编码长度
(6)将原文件中所有字符转成01二进制编码,将二进制编码转成16进制编码写入文件
5.解压缩
其实解压缩的过程就是将刚刚的得到的压缩文件内容读出来,再进行转化,就得到原文件内容啦
(1)从压缩文件中读取压缩标识,即Y,S,判断是否为压缩文件
(2)从压缩文件读出叶子个数
(3)从压缩文件中读出所有叶子按照叶子所对应字符,编码长度(即01的个数 ),转换后的编码长度(即二进制转换成十六进制后所得编码长度),转换后的编码
(4)读出八位对齐补偿长度
(5)读出压缩文件中哈夫曼编码长度
(6)将压缩文件中所有16进制编码,,将16进制编码转成2进制编码
(7)从前向后依次搜索叶子节点的各个编码,将与之对应的字符写入解压缩文件
这个就是压缩和解压缩的全部过程,看着还蛮多的,蛮复杂的,但是想进步就必须要挑战难度高的嘛
代码:
第一部分是建立结构体:
#include
#include
#include
#include
#include
using namespace std;
int i,j,m;
int n,count1;
int count[10000];
#define MAX 10000
char b[10000];
int HfmLength=0;
int q = 0;
typedef struct//存放叶子每个字符,及其权值
{
char ch;
int weight;
} charweight;
charweight flist[256];
typedef struct hfnode//建立哈夫曼树
{
int weight;
char ch;
int parent,lchild,rchild; //双亲,左孩子,右孩子
} HFMNode;
HFMNode HT[600];
typedef struct //存放每个字符及其哈夫曼编码
{
char ch;
char code[300];
int len;
} codeStru;
codeStru cs[300];
int HFMCodeLength;//哈夫曼叶子节点数
char Yasuoqianfile[200];
char *bufFile;//被压缩的文件的各个字节
char *buf;//重新编码后的01串
char *buf1;//压缩存储的01串
char *buf2;
int lengthFile;//被压缩的文件的长度
int length;//编码以字节存储时的长度
short int leng_duiqi;//8位对齐补偿长度
int lengthSum;//补齐8位后的字节长度
int lengthByte;//压缩成按位存储时的长度
第二部分统计叶子节点,及其权值
//统计每个字符的权值
void Statistics(const char *Yasuoqianfile)
{
int count[256];
FILE *fp;
unsigned char ch;
for(i=0;i<256;i++)
count[i]=0;
fp=fopen(Yasuoqianfile,"rb");//打开压缩前文件,即原文件
while(!feof(fp))
{
fread(&ch,1,1,fp);
count[ch]++;//计算相同字符个数,即权值
}
fclose(fp);
for(i=0;i<256;i++)
if(count[i]>0)
{
flist[HfmLength].ch=i;
flist[HfmLength].weight=count[i];
HfmLength++;
}
for(i=0;i
第三部分建立哈夫曼树:
//先选取两个无父母节点的,让剩下的无父母结点的依次和min1,min2比较
void select(int min0,int &min1,int &min2)
{
int i=0;
int temp;
int wetmin1,wetmin2;
while(HT[i].parent!=-1 )
i++;
wetmin1=HT[i].weight;
min1=i;
i++;
while(HT[i].parent!=-1)
i++;
wetmin2=HT[i].weight;
min2=i;
i++;
if(wetmin1>wetmin2)//min1永远为最小值
{
temp=wetmin2;
wetmin2=wetmin1;
wetmin1=temp;
temp=min2;
min2=min1;
min1=temp;
}
for(;i<=min0;i++)
{
if(HT[i].weight
第四部分创建哈夫曼编码
int strLen2( char *str)
{ int k;
for(k=0;str[k]!=0;k++);
return k;
}
//将字符串反转、
void ReverseStr( char *str){
char c;
int len2;
len2=strLen2(str);
for(i=0;ivoid CreateHFCode()
{
int i,j,len;
for(i=0;i
第五部分压缩:
终于到了本篇文章的重头戏,但是希望你对前面的内容可以充分理解,有不懂的地方,可以在下方评论,我很愿意解答的呀~~~
void asc2hex(char *str_in, int len_in, char * str_out)
{
int val,i,c,j,k;
int len_out;
if(len_in%8==0)
len_out=len_in/8;
else
len_out=len_in/8+1;
k=0;
for(i=0;i>Yasuoqianfile;
fp=fopen(Yasuoqianfile,"rb");
if(fp==NULL)//判断内容是否为空
{
cout<<"打开文件%s失败\n"<>Yasuohoufile;
ofp=fopen(Yasuohoufile,"wb");
if(ofp==NULL)
{
cout<<"没有找到目标文件!!!!!!!"<Y,S写入要压缩的文件中作为标记位
cnum=HfmLength;//叶子节点个数
unsigned char ch2;
fwrite(&cnum,2,1,ofp);//将叶子个数写入压缩文件
//从压缩文件中读出所有叶子按照叶子所对应字符,编码长度(即01的个数 ),转换后的编码长度(即二进制转换成十六进制后所得编码长度),转换后的编码
for(i=0;i叶子所对应字符
ch1=cs[i].len;
fwrite(&ch1,sizeof(unsigned char),1,ofp);//编码长度(即01的个数 )
ch2=ch1/8;
if(ch1%8!=0) ch2++;
fwrite(&ch2,sizeof(unsigned char),1,ofp);//转换后的编码长度(即二进制转换成十六进制后所得编码长度)
asc2hex(cs[i].code, cs[i].len, str234);
fwrite(str234,ch2,1,ofp); //转换后的编码
}
int length=0;//总的01个数
for(i=0;i写入八位对齐补偿长度
fwrite(&lengthByte,sizeof(int),1,ofp);//原文件哈夫曼编码长度
for(i=0;i二进制编码转成16进制编码写入文件
} fwrite(bufFile,lengthByte,1,ofp);
fclose(ofp);
cout<<"源文件长度为:"<
第六部分解压缩:
压缩过程已经完成,下面开始解压缩,你看到这里,如果对压缩过程了解的还不是很清楚,那建议你再看几遍压缩代码,在进行下面的解压速。
void hex2asc(char *str_in, int len_in, char * str_out)
{
int i,j,k;
char ch;
k=0;
for(i=0;i=0;j--)
{
str_out[k+j]=(ch%2==0)?'0':'1';
ch=ch>>1;
}
k+=8;
}
}
void jieyasuo()
{
FILE *ifp,*ofp;
char infile[200],outfile[200];
int i,j,k;
char str234[300];
cout<<"请输入要解压的文件名(文件的地址):";
cin>>infile;
ifp=fopen(infile,"rb");
if(ifp==NULL)
{
cout<<"没有找到目标文件!!!!!!"<>outfile;
ofp=fopen(outfile,"wb");
if(ofp==NULL)
{
cout<<"没有找到目标文件!!!!!!!"<从压缩文件中读取压缩标识
if(data[0]!='Y'||data[1]!='S')
{
cout<<"不是压缩文件"<从压缩文件读出叶子个数
for(i=0;i从前向后依次搜索叶子节点的各个编码,将与之对应的字符写入解压缩文件
for(int i1=0;i1
第七部分主函数
int main()
{
int flag=1;
bufFile=( char *)malloc(100000000);
buf=( char *)malloc(100000000);
buf1=( char *)malloc(100000000);
buf2=( char *)malloc(100000000);
for(;flag==1;)
{
cout<<"******文件压缩程序******"<>n;
switch(n)
{
case 1:yasuo(); break;
case 2:jieyasuo() ;break;
case 3:flag=0; cout<<"运行结束!!!" <
这里有些小建议:
因为代码比较长,所以希望你在明白整个压缩解压缩过程后,再进行编写,我的代码是可以运行的,但是因为我不会放图片,所以运行结果后续再加。
整个过程比较复杂,所以刚开始看不懂很正常,我花了10天才看懂呢,所以小可爱不着急,多看几遍,你就能会了,就会感受编程的乐趣。。。
文章到这里也就结束了,内容有点多,但是还是很详细的,希望给那些编程小白一点帮助。可能我的代码存在不足,欢迎各位下方评论指出!!!