文件压缩我总以为很神奇,所以一直觉得这是一个神奇的东西,但碰上神奇的东西我总是想一探究竟,在经过一番探寻答案的过程中,才发现原来文件压缩并非神奇,仅仅是利用了编码的原理转换了原来的内容而已,哈哈,这就像魔术很神奇,但是当你了解到它背后的原理,你就觉得如此普通,所以无论遇到什么看似很难的事情,只要静下心来去研究它,你总会有豁然开朗的那一天,加油吧,少年~
下面让我们一起走进文件压缩的世界吧!
文件压缩
1 什么是文件压缩?
通过某一种机制让文件变小,并且能够通过某种方式对其还原。
2 文件压缩分类?
无损压缩:解压缩之后的文件与源文件相同
有损压缩:解压缩之后的文件与源文件不同
3 为什么要对文件压缩?
节省空间
4 如何实现?
文件:ABBBCCCCCDDDDDDD--16
文件中内容在磁盘中以字节流的方式进行存储---字节---8比特
【等长编码】
00--A
01--B
10--C
11--D
00010101 10101010 10111111 11111111
0x15 0xAA 0xBF 0xFF【不等长编码】【huffman编码】
A: 100
B: 101
C: 11
D: 0压缩:用找到的编码重新改写文件
解压缩:压缩文件 + 字符编码
哈夫曼树:带权路径长度[WPL]最短的二叉树
文件压缩的原理其实就是:基于huffman树编码原理
原理就是这么简单,,,一个字符占一个字节,现在用二进制编码代替之后,一个字符只占三位,也就是说一个字节可以表示两三个字符,所以说一次压缩,就会节省很多字节,也就起到了压缩的作用。
这个项目中,我觉得很重要的三点在于:
创建huffman树【权值w1,w2,,,wn】:
1 先用权值创建n棵只有根节点的二叉树森林【意思是先创建n个节点】
【用到了priority_queue优先级队列】2 选取根节点权值最小的二叉树构建新的二叉树【建小堆,新二叉树根节点权值为左右子树的根节点权值之和】
3 删除使用的两棵根节点权值较小的二叉树
4 将新创建的二叉树添加到二叉树森林中
接下来2-4循环继续,直到二叉树森林中只有一棵二叉树则Huffman树创建成
文件压缩过程:
0 读取源文件,读取源文件中每个字符出现的次数
1 以每个字符出现的次数作为权值,创建huffman树:小堆--优先级队列
2 通过huffman树找每个字符对应的编码
3 用每个字符的新编码重新对源文件进行改写【翻译的过程】
文件解压缩的过程:
1. 从压缩文件中获取源文件的后缀
2. 从压缩文件中获取字符次数的总行数
3. 获取每个字符出现的次数
4. 重建huffman树5. 解压压缩数据
a. 从压缩文件中读取一个字节的获取压缩数据ch
b. 从根节点开始,按照ch的8个比特位信息从高到低遍历huffman树:
该比特位是0,取当前节点的左孩子,否则取右孩子,直到遍历到叶子节点位置,该字符就被解析成功,将解压出的字符写入文件,如果在遍历huffman过程中,8个比特位已经比较完毕还没有到达叶子节点,从a开始执行
c. 重复以上过程,直到所有的数据解析完毕。
详细代码见GitHub:https://github.com/xiaobaiyuan-bit/filecompress.git
接下来就是我在写代码当中碰到的一些BUG和错误,我将这些总结起来:
(1).刚开始写的时候测试发现如果压缩文件中出现了中文,程序就会崩溃,将错误定位到构建哈夫曼编码的函数处,最后发现是数组越界的错误,
因为如果只是字符,它的范围是-128~127,程序中使用char类型为数组下标(0~127),所以字符没有问题. 但是汉字的编码是两个字节,所以可能会出现越界,
解决方法就是将char类型强转为unsigned char,可表示范围为0~255.
(2)文件恢复的时候需要注意那些问题?
有些特殊字符在处理需要注意一下,比如'\n',我的程序中Getline()函数就是读取一行字符,但是若是该字符本身就是一个'\n'呢? 这就非常的棘手了. 因为解压缩之后出现了乱码.
对于这个问题一定好好处理,读取压缩文件时若读到了'\n',则说明该字符就是'\n',应该继续读取它的次数.
好了,基本上就是这些了,总结一句,实践与理论相结合,才会有大进步~