数据结构期末作业-哈夫曼文件压缩

自己的大作业,做的很匆忙。本人文笔不好,只是希望我所写的可以为
读者提供帮助



哈夫曼文件压缩
运行环境:vc++ 6.0 / vs2010
主要算法:哈夫曼压缩
注意:压缩的文本文件和解压缩之后的文本文件都要存放在vc++ 6.0 或者 vs2010 的工程文件夹下面


原理:
哈夫曼编码思想:
①首先遍历要处理的文本文件,得到每个字符的出现的次数;
②将每个字符(以其出现次数为权值)分别构造为二叉树(此时的二叉树只有一个节点);
③取所有二叉树种种字符出现次数最小的二叉树合并为一颗新的二叉树,新二叉树根节点
  的权值等于两个子节点的权值之和,新节点中的字符忽略;
④重复过程③直到所有树被合并为同一棵二叉树
⑤遍历最后得到的二叉树,自顶向下按路径编号,指向左节点的边编号0,指向右节点的边编号1,
  从根到叶节点的所有边上的0和1链接起来,就是叶子节点中字符的哈夫曼编码。


结构体:
typedef struct node
{
    int count;					 //统计字符个数
    unsigned char ch;			 //扫描文本文件的字符
    int parent,lchild,rchild;    //构建二叉树
    char bits[256];				 //二叉树的字符编码‘010101010101...’
}header[512],temp;


一、文件压缩:
①统计词频:读取文件的每个字节,使用整数count统计每个字符出现的次数,
  在统计字符数的时候,对于每一个byte, 有[(unsigned char)byte].count++。
②构造哈夫曼树:根据header[]数组,基于哈夫曼树算法造哈夫曼树,由于构造的过程中每次都要取最小权值的字符,
  所以需要用优先队列来维护每棵树的根节点。
③生成编码:遍历哈弗曼树,得到每个叶子节点中的字符的编码并存入数组header[i].bits;
④存储词频:新建存储压缩数据的文件,首先写入不同字符的个数,然后将每个字符及其对应的词频写入文件。
⑤存储压缩数据:再次读取待压缩文件的每个字节byte,由header[i].bits得到对应的编码(注意每个字符
  编码的长度不一),使用位运算一次将编码中的每个位(BIT)设置到一个char类型的位缓冲中,可能多个编码才能填满一个
  位缓冲,每填满一次,将位缓冲区以单个字节的形式写入文件。当文件遍历完成的时候,文件的压缩也就完成了。
  PS:对于到文件末尾未能填满缓冲区的字符,在后面加上0就行了




存储压缩信息的时候:向新建的压缩文件存储->压缩之前文件的大小(字符个数)、压缩之后的文件大小
哈夫曼编码表、压缩之后的文件。


二:解压缩
首先将文件信息和哈夫曼编码表读取出来;
遍历压缩文件信息;利用函数itoa将读取的信息转换为哈夫曼编码
利用函数 memcmp 将哈夫曼编码转换为字符存储到解压缩的文件当中。


示例:
文本文件存储信息:’ABBCCCDDDD‘
哈夫曼编码之后:
A  '100'
B  '101'
C  '11'
D  '0'
存储->10010110,11111110,000"00000"->转换为数字->150,254,0
将数字以二进制存储到新的文件中
原本文件->10字节
压缩之后->3字节(其他信息)
解压缩->读取 150,254,0
转换为‘0101...’类型的字符串->10010110,11111110,0->补齐(10010110,11111110,00000000)
利用memcmp函数在哈夫曼编码表中逐一查找。
100(A)101(B)101(B)11(C)11(C)11(C)11(C)0(D)0(D)0(D)0(D)0(D)剩下的0可以根据实现记录添加的0的个数然后剔除掉。

/*哈夫曼压缩程序*/
#include 
#include 
#include 
#include 
#include 
#include 
/*程序结构体*/
struct node
{
	int count;
	unsigned char ch;
	int parent,lchild,rchild;
	char bits[256];
}header[512],temp;

long huffmancoding(char inputfile[]);
void transfer_to_file(long sum,char inputfile[],char sto[]);
void uncompress(char sto[],char filename[]);
void print();
void print_history();
int print_info();

long n;
/*快速排序*/
int cmp(const void *a,const void *b)
{
	long *pa = (long*)a,*pb = (long*)b;
	return (int)((*pb)-(*pa));
}

int main()
{
	int i,k;
	char picture[128];
	char input_file[128],info[128],result_file[128];
	long length;
	char ch;
	int flag_file = 0;
	FILE *coding_history;
	char coding_[128];
	printf("   欢迎使用文件压缩器:\n");
	printf("   请选择以下功能:.\n");
	printf(" 1: 压缩文本文件.\n");
	printf(" 2: 解压文本文件.\n");
	printf(" 3: 查看历史记录.\n");
	printf(" 4: 查看编码结果.\n");
	printf(" 0: 退出.\n");
	while(1)
	{
		scanf("%c*%c",&ch);
		if(ch == '0')
		{
			printf("欢迎下次再次使用.\n");
			exit(1);
		}
		else if(ch=='1')
		{
			flag_file = 1;
			print();
			printf("请输入要压缩的文件名.\n");
			scanf("%s",input_file);
			printf("请输入压缩后的文件名.\n");
			scanf("%s*%c",info);
			/*哈夫曼编码*/
			length = huffmancoding(input_file);
			/*文件压缩*/
			transfer_to_file(length,input_file,info);
			scanf("%c*%c",&ch);
		}
		else if(ch =='2')
		{
			if(!print_info())
			{
				scanf("%c*%c",&ch);
			}
			else
			{
				printf("请输入要解压的文件.\n");
				scanf("%s",info);
				printf("请输入解压后的文件名.\n");
				scanf("%s",result_file);
				/*文件解压*/
				uncompress(info,result_file);
				scanf("%c*%c",&ch);
			}
		}
		else if(ch == '3')
		{
			print_history();
			scanf("%c*%c",&ch);
		}
		else if(ch == '4')
		{
			if(flag_file==1)
			{
				coding_history = fopen("coding.txt","r");
				if(coding_history == NULL)
				{
					printf("文件打开失败.\n");
					exit(1);
				}
				while(!feof(coding_history))
				{
					fscanf(coding_history,"%s",coding_);
					printf("%s\n",coding_);
				}
				fclose(coding_history);
				scanf("%c*%c",&ch);
				flag_file = 0;
			}
			else if(flag_file == 0)
			{
				printf("尚未进行编码,请编码之后选择本功能.\n");
				scanf("%c*%c",&ch);
			}

		}
		else
		{
			printf("命令不存在,请重新输入.\n");
			scanf("%c*%c",&ch);
		}

	}

	return 0;
}

/*压缩程序*/
long huffmancoding(char inputfile[])
{
	FILE *coding_result;
	unsigned char ch;
	int i,j;
	long m,pt,prelength,ioflength,tmp;
	long min;
	FILE *input_file;

	input_file = fopen(inputfile,"rb");
	if(input_file==NULL)
	{
		printf("  文件打开失败.\n");
		exit(1);
	}
	prelength=0;

	while(!feof(input_file))
	{
		fread(&ch,1,1,input_file);
		header[ch].count++;
		prelength++;
	}

	prelength--;
	ioflength = prelength;
	header[ch].count--;

	for(i=0;i<512;i++)
	{
		if(header[i].count!=0) header[i].ch=(unsigned char)i;
		else header[i].ch=0;
		header[i].parent=-1;header[i].lchild=header[i].rchild=-1;
	}
	qsort(header,512,sizeof(struct node),cmp);

	for(i=0;i<256;i++) if(header[i].count==0) break;
	n=i;
	m=2*n-1;
	for(i=n;iheader[j].count)
			{
				pt=j;
				min=header[j].count;
				continue;
			}
		}
		header[i].count=header[pt].count;
		header[pt].parent=i;
		header[i].lchild=pt;
		min=32767;
		for(j=0;jheader[j].count)
			{
				pt=j;
				min=header[j].count;
				continue;
			}
		}
		header[i].count+=header[pt].count;
		header[i].rchild=pt;
		header[pt].parent=i;
	}
	for(i=0;i=8)
		{
			for(i=0;i<8;i++)
			{
				if(buf[i]=='1') ch=(ch<<1)|1;
				else ch=ch<<1;
			}
			fwrite(&ch,1,1,out_file);
			pt++;
			strcpy(buf,buf+8);
			k=strlen(buf);
		}
		if(f==prelength)break;
	}
	if(k>0)
	{
		strcat(buf,"00000000");
		for(i=0;i<8;i++)
		{
			if(buf[i]=='1') ch=(ch<<1)|1;
			else ch=ch<<1;
		}
		fwrite(&ch,1,1,out_file);
		pt++;
	}

	fseek(out_file,4,SEEK_SET);
	fwrite(&pt,sizeof(long),1,out_file);
	fseek(out_file,pt,SEEK_SET);
	fwrite(&n,sizeof(long),1,out_file);
	for(i=0;i0) m=p/8+1;
		else m=p/8;
		for(j=0;jf;l--)
			{
				strcat(header[i].bits,"0");
			}
			strcat(header[i].bits,buf);
		}
		header[i].bits[p]=0;
	}

	for(i=0;istrlen(header[j].bits))
			{
				temp=header[i];
				header[i]=header[j];
				header[j]=temp;
			}
		}
	}

	p=strlen(header[n-1].bits);
	fseek(ifp,8,SEEK_SET);
	m=0;
	bx[0]=0;
	while(1)
	{
		while(strlen(bx)<(unsigned int)p)
		{
			fread(&ch,1,1,ifp);
			f=ch;
			itoa(f,buf,2);
			f=strlen(buf);
			for(l=8;l>f;l--)
			{
				strcat(bx,"0");
			}
			strcat(bx,buf);
		}
		for(i=0;i


附上程序流程图


还有哈夫曼压缩原理


你可能感兴趣的:(数据结构,c语言)