基于哈夫曼编码的文件压缩项目(2),2024年最新4个改变你编程技能的小技巧

template
class HuffmanTree
{
typedef HuffmanTreeNode Node;
public:

class Compare
{
public:
// 优先级队列中元素大小按降序排列
bool operator()(const Node* left, const Node* right)
{
return left->_weight > right->_weight;
}
};

HuffmanTree()
:_root(nullptr)
{}

HuffmanTree(const vector& vw, const W& invaild)
{
// 使用节点权值构建二叉树节点(只有根节点的二叉树森林)
// 使用堆(优先级队列)来储存这些二叉树森林
std::priority_queue<Node*, vector<Node*>, Compare> q;
for (auto e : vw)
{
// 当字符出现次数不为0时,插入队列
if (e != invaild)
{
q.push(new Node(e));
}
}

while (q.size() > 1)
{
// 取出两个队列顶部元素作为左右两子树
Node* left = q.top();
q.pop();

Node* right = q.top();
q.pop();

// 将left和right作为新节点的左右字数
Node* parent = new Node(left->_weight + right->_weight);

// 链接父节点和左右子节点
parent->_left = left;
left->_parent = parent;

parent->_right = right;
right->_parent = parent;

// 将新节点重新放入二叉树森林中
q.push(parent);
}
_root = q.top();
}

~HuffmanTree()
{
Destroy(_root);
}

private:
void Destroy(Node* root)
{
if (root)
{
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
}

private:
Node* _root;
};

六、文件压缩

压缩过程需要要经过四个步骤:

1、统计字符出现的次数

对于获取文件中每个字节出现的次数,采用一个含有256个结构体类型的数组来保存较为方便。因为文件在磁盘中都是以字节的方式来进行存储的,可以用每个字符对应的ASCII码值来作为数组的下标,只需遍历文件中的每一个字节,遇到对应ASCII码的元素对结构体中的count进行++即可。如下图,数组的下标与字符的ASCII码值一一对应:

2、利用统计的次数作为权值来构建Huffman树

在构建哈夫曼树时,直接调用之前写过的创建树的方式即可,但需要注意一点:
哈夫曼树每个节点类型为结构体类型,因此在定义结构体的地方,需要对运算符(+,>,!=,==)进行重载,让编译器知道count之间的比较方式。

3、获取哈夫曼编码

我们现在已经有了根据频次信息创建的Huffman树,现在只需要遍历Huffman树来获取编码即可。对于编码的获取方式,可以从根节点遍历到叶子节点来获取,也可以从叶子节点向上找到根节点获取。**Huffman树节点的表示方法为孩子双亲表示法。**通过这种方式获取的编码最后需要进行逆置才是最终结果,下图是获取编码后的结果,与期望的编码相同。

4、写压缩文件头部信息(用来解压的信息)

压缩文件中必须含有用来解压文件的信息,主要包含以下几点:

  1. 原文件后缀名
  2. 字节频次信息的总行数(方便对数据进行操作)
  3. 原文件字节出现的频次信息
  4. 压缩数据

5、用获取到的编码对源文件进行改写

前三个步骤进行完之后,只需改写文件即可。还是需要循环读取文件内容,采取位操作按位或的方式将Huffman编码放置到比特位当中。
步骤:1.定义bits = 0,先让bits左移一位,再判断Huffman编码的每个比特位是否为1,为1则让ch与Huffman编码为1比特位进行或操作,这样就能将文件按照编码的方式改写。

七、文件解压缩

1、读取解压缩所用到的信息

在解压缩过程中用到的信息:

  1. 获取原文件后缀名
  2. 获取频次信息的总行数
  3. 获取原文件字节出现的频次信息
  4. 获取压缩数据
2、还原huffman树

有了上面获取的信息,就可以对哈夫曼树进行恢复,继续进行解压缩的过程了,恢复哈夫曼树的过程相对简单,基于之前写过的构建哈夫曼树的方法,只需对文件中压缩信息做相对应的处理。

3、解压缩文件

解压缩过程就是:根据压缩数据来遍历恢复的哈夫曼树,压缩数据为1就往右子树走,为0就往左子树走,知道走到叶子节点。通过以上所有步骤就能够完成压缩与解压缩的所有过程了。
这里还有一个小问题,压缩方法不能处理文件中的汉字,因为汉字是采用UTF-8编码进行编排的,它的编码不在ASCII码表中。因此需要将所有的char类型改变为unsigned char类型!!!

关于具体解压缩的代码(最终代码),请参考:

文件压缩项目: 基于哈夫曼编码的文件压缩源码分享 (gitee.com)icon-default.png?t=N7T8https://gitee.com/Bai-Yun-Shuai/file-compression

总结

以下是我总结完成过程中注意的一些问题:
1、创建哈夫曼树时,使用优先级队列来保存字符出现的频次信息时,优先级队列的比较方式需要自己进行给出,使用仿函数的方法让比较方式为每个节点中的权值。

2、在使用结构体来创建哈夫曼树时,需要对+,>,==,!=,进行重载,重载的目的:让编译器知道权值中的count之间的比较方式

3、在构建哈夫曼树时,发现数组中还存放着许多字符出现次数为0的节点,需要进行过滤,设置第二个参数invalid来过滤权值为0的节点。

4、再次读取文件时,需要将文件指针的位置重置到文件头部,使用fseek函数或者rewind函数。

5、在解压缩时,不一定是8个比特位都需要解压缩,因此需要增加判定条件,只要解压缩后的字节数与原文件相等就停止解压缩。

6、压缩和解压缩代码写完后,需要讲char类型改成unsigned char类型,因为原文件可能出现汉字,汉字对应的是UTF-8编码,不在ASCII码表中。

7、在测试多行数据压缩时,又出现了问题,原因是当读取到\n换行时,并没有将换行所在的数据GetLine,因此解压缩时数据少了一行。

8、在进行大量数据测试时出现问题,发现解压缩一部分内容就停止了,部分数据丢失,但已经解压缩的部分内容与原文件一样。原因是解压缩文件是二进制文件(即压缩结果是二进制数字),二进制文件中可能会出现-1(FF),当为-1时就默认读到了文件末尾就结束解压缩过程了。
改进方法是:
将所有打开文件(fopen)中文件的读写方式由"r",“w"改为二进制读写式"rb”“wb”

9、经过测试,发现对不同的文件(文本文件,二进制文件),图片,视频都能够进行压缩,但哈夫曼编码的文件压缩对不同种类的压缩率是不同的。
对于txt/c/cpp类后缀的文本文件,压缩率较高在30%~50%
对于png/jpg类后缀的图片,压缩率一般在70%~90%
对于exe类后缀的可执行文件,压缩率较低在80%~90%

10、在对文件进行多次压缩时,压缩结果的大小不是每次会减少,有一个限度。
同时,哈夫曼编码的压缩方法也是可能会出现压缩结果变大的可能性。当文件中字节的种类偏多,并且字节出现的次数比较平均的情况下,压缩效率就会变差,因为统计字节出现的频次信息时,各个字节出现的次数差不多,构建出来的哈夫曼树就接近平衡二叉树,效果就会不理想。当然,也会出现压缩后文件变大的情况:当哈夫曼编码的平均长度大于8字节时,压缩文件就会变大。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数大数据工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)
img

712571009894)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)
[外链图片转存中…(img-KFwsPkYw-1712571009894)]

  • 16
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值