基于Huffman算法实现文件压缩解压缩(C语言)

1 篇文章 0 订阅

一、实现步骤

  1. 统计源文件中字符种类和频率
  2. 建立Huffman编码树
  3. 生成Huffman编码表
  4. 压缩文件时,字符匹配编码,将编码写入压缩后文件
  5. 解压缩文件时,读取编码,匹配编码表中的字符,写入解压缩后的文件

二、读取文件

为了能够处理任何格式的文件,采用二进制方式读写文件,以一个无符号字符(unsigned char)的8位类型为一个处理单元,最多有0~255种,即256种。

三、字符频率的统计

两种方案:

  1. 链表存储,每扫描到一种新字符就动态分配内存。链表在需要时分配内存,这样节省内存,但是每读取一个字符就要扫描一次链表,当字符很多时,时间效率太低,最坏达到O(n^2)。
  2. 利用桶排序的思想。因为仅有256种字符,使用静态内存,定义一个长度为256的数组,数组下标对应字符种类,不需要扫描数组就可以找到每一类字符的位置,每次读取一个新字符时,根据下标直接将相应数组元素的频率加一,时间效率更好。因为不是每一种字符都会出现,所以,统计完字符频率后,对它进行排序,降序排列,将字符频率为0的元素删除。

四、建立Huffman森林

Huffman节点包含字符种类、权重、父节点和左右孩子的信息。对每一种字符,分别建立一棵单节点的Huffman树,权重取作该字符的频率。将每一个Huffman节点的成员进行初始化。

五、建立Huffman编码树

每次从Huffman森林中选取两棵根节点权重最小的树,创建一个新节点,并分别以这两棵树作为其左右子树,合并为一棵更高的树,新树根节点的权重取作二者权重之和,设置根节点的左右孩子和两棵子树的父亲信息。这一选取、合并的过程反复进行,每经过一轮迭代,Huffman森林中的树就减少一棵,当最终森林中仅包含一棵树时,就是Huffman编码树。

六、生成Huffman编码

每一类字符对应一个Huffman编码,从叶节点(字符所在节点)从下往上生成每一类字符的编码,左’0’右’1’。为了得到正确的编码,定义一个缓存字符数组,从后往前保存,然后从前往后将编码复制到对应的编码域中。对于缓存字符数组的大小,由于字符种类最多有256种,即建立的Huffman编码树最多有256个叶节点,即树的最大深度为255,编码最长是255,所以为缓存字符数组分配256个字节的空间,最后一位用于保存字符串结束标志’\0’。

七、文件压缩

上面建立编码树时以8位为一个单元编码,压缩时也以8位为一个处理单元。
首先,将字符种类、频率和编码表存储在压缩目标文件中,供解压时使用。
然后,以二进制形式打开源文件,每次读取一个8位的无符号字符,循环扫描匹配存储于Huffman编码树中编码,
由于编码长度不定,需要一个编码缓存,待编码满足8位后才写入,文件结束时缓存中可能不足8位,在后面补0,补足8位后写入文件。
在Huffman节点中,编码的每一位以字符形式保存,占用空间大,不可以直接写入压缩文件,需要转换为二进制形式写入。利用C语言的位操作,与、或、移位,来实现,每匹配一位,用‘或’操作存入低位,并左移1位,为下一位腾出位置,依此循环,满足8位就写入一次。

八、文件解压缩

以二进制形式打开压缩文件。
首先,读取文件前端的字符种类数目,据此分配内存空间,随后读取字符-编码表保存到分配的节点中,再读取文件长度
然后,以8位为单元,读取随后的编码匹配对应的字符,这里依然使用前面压缩时用的方法,即C语言的位操作,同0x80进行与操作,判断8位字符的最高位是否为’1’,对比一位后,左移一位,将最高位移除,次高位移到最高位,依次对比。这次是编码向字符反向对比,与压缩时相反,需要用读取的编码逐位与编码表中的编码进行对比,对比一位后,增加一位再对比,每次对比都是一个循环(与每个字符的编码对比),时间效率低。
将Huffman编码树保存在文件中,解码时,从树根到叶节点对比编码,只要按照编码对树进行遍历,到达叶节点后即是一个字符,然后返回根节点,继续下一次遍历,时间效率高。
然而树节点中有字符、编码、父亲、左右孩子,而且父亲和孩子都是整型,占用空间大,会导致压缩文件变大。可以只存储字符以及对应的频率,解码时读取字符频率序偶即可重建Huffman编码树,这样节省了空间。
虽然,重建Huffman编码树也要花费一定时间,但相对上面的与编码表匹配的方法,时间效率更高。

  • 6
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值