基于哈夫曼树的文件压缩

一个编程小白成功的完成了哈夫曼树的文件压缩,过程可以说是很艰辛了。好啦~废话就不多说了,下面开始正式的讲解,过程+代码。

工具: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天才看懂呢,所以小可爱不着急,多看几遍,你就能会了,就会感受编程的乐趣。。。

文章到这里也就结束了,内容有点多,但是还是很详细的,希望给那些编程小白一点帮助。可能我的代码存在不足,欢迎各位下方评论指出!!!




你可能感兴趣的:(基于哈夫曼树的文件压缩)