基于C语言利用哈夫曼树实现文件压缩的问题

 更新时间:2021年08月04日 10:05:37   作者:Small Program  
哈夫曼编码是一种编码方式,又称“霍夫曼编码”,其是可变字长的编码(VCL)的一种,这篇文章主要介绍了基于C语言利用哈夫曼树实现文件压缩,需要的朋友可以参考下

一、哈夫曼树

        具有n个权值的n个叶子结点,构造出一个二叉树,使得该树的带权路径长度(WPL)最小,则称此二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。

注意:哈夫曼树是带权路径长度最短的树,且权值越大的叶子结点离根结点越近。

二、哈夫曼编码

        哈夫曼编码是一种编码方式,又称“霍夫曼编码”,其是可变字长的编码(VCL)的一种,是由霍夫曼于1952年提出的一种编码方式,有时被称为最佳编码,一般称为Huffman编码

那么我们为什么要使用哈夫曼编码进行压缩?

首先原因在于,传统压缩方式中,字符的编码是以等字长的方式进行压缩,相比于哈夫曼编码的可变字长而言压缩率会降低。其次,传统方式中可能会出现某些字符的编码相同,存在二义性,相比于哈夫曼编码的唯一性。可以大大提高正确性。

2.1 如何得到哈夫曼编码

        通过已经构造的哈夫曼树,结点左分支记为二进制数“0”,结点右分支记为二进制数“1”。从根结点向下到该叶子结点路途经过的数字拼接成为的编码即为该字符的哈夫曼编码。现通过以下图例进行演示。

此时各字符对应的哈夫曼编码可表示为:

A:00        B:01        C:1

三、哈夫曼树实现文件压缩基本步骤

3.1 基本实现流程

3.2 使用文件输入流读取需压缩的文件,并判断该文件是否存在

存在后执行接下来的步骤,不存在提示用户,效果图如下

3.3 读取,统计字符

        用户输入正确文件路径且存在时使用文件输入流读取文件,将文件放于一个字符数组中(注意字符数组开辟空间应足够大)。统计出现的字符及其出现的次数。

char ch;
		while(feof(fp) == 0){
			fscanf(fp,"%c",&ch);
			str[ch]++;
		}
		printf("\n");
		for(i = 0; i < 1024; i++){
			if(str[i] != 0){
				count[n++] = i; 
			}
		}

3.4 构造哈夫曼树

注:由于后续会使用此哈夫曼编码,为了方便获取,这里将求出的哈夫曼编码放置于一个二维数组中。

 CreateHT(ht,node);
		char strarr[256][256];
		int aa,bb;
		for(aa=0; aa<256;aa++)
			for(bb=0;bb<256;bb++)
				strarr[aa][bb]=-1; 
		
		for(k = 0;k < node;k++){
			huffmanCode(ht,k,strarr[k]);
		}
void CreateHT(HTNode *ht, int n){
	int i, j, lnode, rnode n1, n2;
	for(i = n; i < 2*n-1; i++){
		lnode = 65432; rnode = lnode;
		n1 = 0; n2 = 0;
		for(j = 0; j < i; j++){
			if(ht[j].parent == -1){
				if(ht[j].weight <lnode){
					rnode = lnode;
					n2 =n1;
					lnode = ht[j].weight;
					n1 = j; 
				} else if(ht[j].weight < rnode){
					rnode = ht[j].weight;
					n2 = j; 
				}
			} 
		}
		ht[n1].parent = i;
		ht[n2].parent = i;
		
		ht[i].weight = lnode+rnode;
		ht[i].parent = -1;
		ht[i].lchild = n1;
		ht[i].rchild = n2;
	}
}

3.5 根据构造哈夫曼树得到哈夫曼编码

        此时我们使用3.3中统计的字符作为叶子结点,其出现次数作为权值,构造哈夫曼树求出哈夫曼编码。下面以此段英文为例,得到每个字符对应哈夫曼编码。

You can take away our money,house,car,or even our clothes and we can surive.But if our health was taken away,we would surely die.That is why we always try to eat in a healthy way and excise regularly..

3.6 替换字符为其哈夫曼编码

        用户输入压缩至的文件路径,将替换好的0 1数字字符数组写入该文件。由于C语言没有基于字符串的操做,所以我们替换时只能通过对字符数组进行操作,涉及到了被替换字符串长度替换字符串长度的问题,以及字符数组的长度是否充足。由于此次替换被替换字符串只是一个字符,且字符数组开辟空间足够大。所以只考虑替换字符串长度,即字符对应哈夫曼编码的长度,在这里使用一个方法来进行替换。

void replace(char oldstr[], char key[], char rep[]){
	int oldstrLen, lengthOfKey, lengthOfRep, i, j , flag;
	char tmp[1000]; 
	oldstrLen = strlen(oldstr);
	lengthOfKey = strlen(key);
	lengthOfRep = strlen(rep);
 
	for( i = 0; i <= oldstrLen - lengthOfKey; i++){
		flag = 1;
		for(j = 0; j < lengthOfKey; j++){
			if(oldstr[i + j] != key[j]){
				flag = 0; 
				break;
			}
		}
		if(flag){
			strcpy(tmp, oldstr);
			strcpy(&tmp[i], rep);
			strcpy(&tmp[i + lengthOfRep], &oldstr[i + lengthOfKey]);
			strcpy(oldstr, tmp);
			i += lengthOfRep - 1;
			oldstrLen = strlen(oldstr);
		}
	}
}

替换完成后的字符数组使用输出流写入用户输入文件路径。

3.7 进制转换

        将文件中每八位二进制数转换为十进制数,再转换为其对应的ASCII码值,最后不足八位的二进制数以0补齐,实现文件压缩。

3.7.1 进制转换方法

int retTen(char str[]){
	int ten = 0;
	int t = 0;
	for(t = 0;t < strlen(str);t++){
		ten += (str[t]-'0') * pow(2,strlen(str) - t - 1); 
	} 
	return ten;
}

3.7.2 实现效果

3.8 计算压缩率

        最后为验证是否实现压缩,可通过计算文件压缩率来验证。其中,压缩率=原文件字节大小/压缩后文件大小。

int oldFileSize=getFileSize(path);
int newFileSize=getFileSize(Gopath);
		
printf("\n");
printf("压缩成功!,文件已压缩至%s\n",Gopath); 
printf("该文件压缩率为:%.2lf\n",(double)newFileSize/(double)oldFileSize);

注:注意计算压缩率时的类型转换

到此这篇关于基于C语言利用哈夫曼树实现文件压缩的文章就介绍到这了,更多相关C语言实现文件压缩内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++实现inline hook的原理及应用实例

    C++实现inline hook的原理及应用实例

    这篇文章主要介绍了C++实现inline hook的原理及应用,需要的朋友可以参考下
    2014-08-08
  • OpenCV利用高斯模糊实现简单的磨皮美颜效果

    OpenCV利用高斯模糊实现简单的磨皮美颜效果

    这篇文章主要介绍了通过OpenCV中的高斯模糊以及双边模糊来实现一个简单的磨皮美颜效果,文中的讲解很详细,感兴趣的同学可以学习一下
    2021-12-12
  • c语言:金币阵列的问题

    c语言:金币阵列的问题

    本文介绍了关于c语言:金币阵列的问题,需要的朋友可以参考一下
    2013-03-03
  • C++函数的嵌套调用和递归调用学习教程

    C++函数的嵌套调用和递归调用学习教程

    这篇文章主要介绍了C++函数的嵌套调用和递归调用学习教程,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • C++实用库之字节流合成器

    C++实用库之字节流合成器

    在处理跨平台的数据交换或网络通信时,字节流的重要性更加突出,不同的系统可能有不同的字节序(大端序或小端序),因此在发送和接收字节流时,可能需要考虑字节序的转换,这篇文章主要介绍了C++实用库之字节流合成器,需要的朋友可以参考下
    2024-04-04
  • Qt增加版本公司等信息两种方式

    Qt增加版本公司等信息两种方式

    在项目中生成exe或者动态库过程中可能需要加入公司信息、版本号、说明等等,下面这篇文章主要给大家介绍了关于Qt增加版本公司等信息的两种方式,需要的朋友可以参考下
    2024-01-01
  • C++ 将字符串值赋给CHAR数组的实现

    C++ 将字符串值赋给CHAR数组的实现

    这篇文章主要介绍了C++ 将字符串值赋给CHAR数组的实现,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • C++基于easyx图形库实现推箱子游戏

    C++基于easyx图形库实现推箱子游戏

    这篇文章主要为大家详细介绍了C++基于easyx图形库实现推箱子游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • C++映像劫持后门实例分析

    C++映像劫持后门实例分析

    这篇文章主要介绍了C++映像劫持后门,实例分析了C++映像劫持后门的原理与相关实现技巧,有助于进一步了解后门的原理,需要的朋友可以参考下
    2015-04-04
  • C++中模板和STL介绍详解

    C++中模板和STL介绍详解

    今天小编就为大家分享一篇关于C++模板和STL的介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2021-09-09

最新评论