基于Huffman算法的文件解压缩
基于Huffman算法和LZ77算法的文件压缩(三)
基于Huffman算法和LZ77算法的文件压缩(一)和
基于Huffman算法和LZ77算法的文件压缩(二)讲解Huffman压缩的基本原理和文件压缩的整个过程,接下来讲解文件解压缩的过程
一、 利用huffman编码对源文件进行解压缩
解压缩的整个流程:
- 从压缩文件中获取源文件的后缀
- 从压缩文件中获取字符次数的总行数
- 获取每个字符出现的次数
- 重建huffman树
- 解压缩
二. 从压缩文件中获取源文件的后缀
如果想要解压缩文件,那么就需要Huffman树
,如果需要Huffman树,就需要字符频度信息
,最后还要把解压缩出来的数据保存在文件中,就需要获取文件后缀信息
。那么这些标记信息已经在压缩文件的时候写入到压缩文件当中,接下来直接读取即可。
为了方便读取一行,先封装一个函数:
//读一行函数
void FileCompressHuffman::ReadLine(FILE* fIn, std::string& strInfo)
{assert(fIn);//注意这里的feof是针对二进制文件的,因为我们为了解决可以压缩汉字的相关问题,//选择以二进制的方式打开,并不是以文本文件的方式打开,文本文件的结束标识是EOF,//而二进制文件的结束标识是FEOFwhile (!feof(fIn)){char ch = fgetc(fIn);if (ch == '\n')break;strInfo += ch;}
}
注意:
- 注意这里的
feof是针对二进制文件
的,因为我们为了解决可以压缩汉字的相关问题,选择以二进制的方式打开,并不是以文本文件的方式打开,文本文件的结束标识是EOF,而二进制文件的结束标识是FEOF
读取文件后缀
//文件后缀std::string strFilePostFix;ReadLine(fIn, strFilePostFix);
三. 从压缩文件中获取字符次数的总行数
//读取文件数据的行数std::string strCount;ReadLine(fIn, strCount);int lineCount = atoi(strCount.c_str());
四. 获取每个字符出现的次数信息
for (int i = 0; i < lineCount; ++i) {//记录读取每一行的数据std::string strchCount;ReadLine(fIn, strchCount);//如果读到的数据为空,说明可能读到了换行符if (strchCount.empty()) {strchCount += '\n';ReadLine(fIn, strchCount);}//因为数据的格式是 A:3,记录读取到的相关字符信息方便构建哈夫曼树,_fileInfo[(unsigned char)strchCount[0]]._count = atoi(strchCount.c_str() + 2);}
注意:
- 如果文件当中含有多行(即有人为换行),需要再向后读取一行,因为我们封装的ReadLine函数是遇到换行就break掉,没有成功把换行读到缓冲区当中,所以需要
if (strchCount.empty())
判断,如果没有读取到任何字符,就代表读取到了换行符
,就把换行符追加到strchCount,再向下读取一行获取换行符的出现次数
五. 重建huffman树
HuffmanTree<CharInfo> t;t.CreateHuffmanTree(_fileInfo, CharInfo(0));//构建哈夫曼树
六. 解压缩
//解压缩后的文件名std::string newFileName = "3" + strFilePostFix;FILE* fOut = fopen(newFileName.c_str(), "wb");if(fOut == nullptr){perror("open file is error\n");return ;}char *pReadBuff = new char[1024];char ch = 0;HuffmanTreeNode<CharInfo>* pCur = t.GetRoot();//拿到根节点,也就拿到了整个哈夫曼树的权值之和size_t fileSize = pCur->_Weight._count;//记录待解压的总字节数size_t unCount = 0;//记录已经解压字符的个数while (1) {size_t rdsize = fread(pReadBuff, 1, 1024, fIn);if (rdsize == 0) {break;}for (size_t i = 0; i < rdsize; ++i) {ch = pReadBuff[i];for (int pos = 0; pos < 8; ++pos) {//0x80--->1000 0000 所以是用来判断一个字节的高位是1还是0的//规定 1--->往哈夫曼树的右子树走 0--->往哈夫曼树的左子树走if (ch & 0x80) {pCur = pCur->_pRight;}else {pCur = pCur->_pLeft;}ch <<= 1;//如果pCur左右孩子都为空,说明走到叶子节点了,就可以获取到具体的字符了if (nullptr == pCur->_pLeft && nullptr == pCur->_pRight) {++unCount;fputc(pCur->_Weight._ch, fOut);pCur = t.GetRoot();//因为每次不一定刚好是整字节数,所以防止多读,需要进行判断if (unCount == fileSize)break;}}}
注意:
- 规定1往右走,0往左走,当走到叶子节点的时候,就把解压出来的字符写入到解压缩文件当中
- 因为存储字符信息的时候是一个字节一个字节存储的,即8bite,如果我们知道每个bite位为0还是为1,就可以控制其在Huffman中是朝左走,还是朝有走,最后走到叶子节点拿到字符信息,所以我们 &0x80(1000 0000) ,如果结果为真代表该bite位为1,反之为0,这样就可以控制走 向了
- 最后还要 根据Huffman树根节点的权值信息来获取到整个文件的大小,防止最后一个bit位解压过头, 在每成功解压出一个数据后,就对++unCount,最后判断unCount和fileSize的大小,如果相等就代表已经全部解压出来,后序bit位不是压缩数据
- 最最最最后再说一次,保存字符信息的相关变量要用unisigned char类型,不能用char类型,否则在压缩或解压缩汉字时会报错
- 文本文件的末尾标志是EOF(-1),而压缩文件的结果是有可能FF的情况(即1111 1111 …,32个1),那么在文本文件当中在if(!EOF())if(!EOF())if(!EOF())判断的时候就会意外终止,所以需要以二进制的方式读写文件
解压缩完整代码
:
//解压缩
void FileCompressHuffman::UnCompressFile(const std::string& path) {FILE* fIn = fopen(path.c_str(), "rb");if (nullptr == fIn) {//assert(false);perror("open file is error");return;}//文件后缀std::string strFilePostFix;ReadLine(fIn, strFilePostFix);//读取文件数据的行数std::string strCount;ReadLine(fIn, strCount);int lineCount = atoi(strCount.c_str());for (int i = 0; i < lineCount; ++i) {//记录读取每一行的数据std::string strchCount;ReadLine(fIn, strchCount);//如果读到的数据为空,说明可能读到了换行符if (strchCount.empty()) {strchCount += '\n';ReadLine(fIn, strchCount);}//因为数据的格式是 A:3,记录读取到的相关字符信息方便构建哈夫曼树,_fileInfo[(unsigned char)strchCount[0]]._count = atoi(strchCount.c_str() + 2);}HuffmanTree<CharInfo> t;t.CreateHuffmanTree(_fileInfo, CharInfo(0));//构建哈夫曼树//解压缩后的文件名std::string newFileName = "3" + strFilePostFix;FILE* fOut = fopen(newFileName.c_str(), "wb");if(fOut == nullptr){perror("open file is error\n");return ;}char *pReadBuff = new char[1024];char ch = 0;HuffmanTreeNode<CharInfo>* pCur = t.GetRoot();//拿到根节点,也就拿到了整个哈夫曼树的权值之和size_t fileSize = pCur->_Weight._count;//记录待解压的总字节数size_t unCount = 0;//记录已经解压字符的个数while (1) {size_t rdsize = fread(pReadBuff, 1, 1024, fIn);if (rdsize == 0) {break;}for (size_t i = 0; i < rdsize; ++i) {ch = pReadBuff[i];for (int pos = 0; pos < 8; ++pos) {//0x80--->1000 0000 所以是用来判断一个字节的高位是1还是0的//规定 1--->往哈夫曼树的右子树走 0--->往哈夫曼树的左子树走if (ch & 0x80) {pCur = pCur->_pRight;}else {pCur = pCur->_pLeft;}ch <<= 1;//如果pCur左右孩子都为空,说明走到叶子节点了,就可以获取到具体的字符了if (nullptr == pCur->_pLeft&&nullptr == pCur->_pRight) {++unCount;fputc(pCur->_Weight._ch, fOut);pCur = t.GetRoot();//因为每次不一定刚好是整字节数,所以防止多读,需要进行判断if (unCount == fileSize)break;}}}}fclose(fIn);fclose(fOut);delete[] pReadBuff;
}
基于Huffman压缩还存在的问题:
- 无法压缩视屏、图片、音频文件,只能压缩文本文件,压缩视频、图片等文件会发现压缩后等文件变大
基于Huffman算法的文件解压缩相关推荐
- 基于LZ77算法的文件解压缩项目缺陷分析
基于Huffman算法和LZ77算法的文件压缩(七) 基于Huffman算法和LZ77算法的文件压缩(六)已经讲解完文件压缩的过程,本文讲解文件解压缩的过程和大文件处理方式 一.解压缩的流程 LZ77 ...
- 基于Huffman算法实现文件压缩解压缩(C语言)
一.实现步骤 统计源文件中字符种类和频率 建立Huffman编码树 生成Huffman编码表 压缩文件时,字符匹配编码,将编码写入压缩后文件 解压缩文件时,读取编码,匹配编码表中的字符,写入解压缩后的 ...
- 基于LZ77算法的文件压缩铺垫
基于Huffman算法和LZ77算法的文件压缩(四) 本文开始讲解LZ77算法,会用到哈希,哈希原理详解 我们在基于Huffman算法和LZ77算法的文件压缩(一)当中总体介绍了Huffman算法和L ...
- huffman算法实现文件的压缩与解压
本文采用哈夫曼编码的方式进行文件的压缩和解压缩,主要原理是通过huffman编码来表示字符,出现次数多的编码短,出现次数少的编码长,这样整体而言,所需的总的bit位是减少的.但是当大部 ...
- 单向散列函数概述并基于MD5算法对文件哈希值实时监测
1.如何验证文件是否被修改过 只生成一个指纹文件,对指纹文件进行验证 当已经存储的文件被修改之后,指纹文件就会跟着变化,即生成一个单向散列函数 任意长度的数据都对应固定长度的散列值–减少匹配开销 散列 ...
- 基于LZ77算法的文件压缩收尾
基于Huffman算法和LZ77算法的文件压缩(六) 前面基于Huffman算法和LZ77算法的文件压缩(四) 基于Huffman算法和LZ77算法的文件压缩(五) 已经充分讲解LZ77到基本原理和实 ...
- 基于LZ77算法的文件压缩
基于Huffman算法和LZ77算法的文件压缩(五) 基于Huffman算法和LZ77算法的文件压缩(四)已经讲解LZ77算法到基本原理和压缩过程. 本文详细讲解文件压缩过程当中的问题 一.文件压缩的 ...
- BUAA数据结构作业——基于Huffman码的文件压缩工具
目录 前言 题目 问题描述 输入形式 输出形式 样例 问题分析 代码实现 前言 大家好哇!今天给大家分享一道Huffman码的实战应用题.Huffman码来源于Huffman树,假设二叉树有m个叶结点 ...
- 基于Huffman树的文件压缩(详细)
文件压缩 开发平台:Visual Studio 2008 开发技术:哈夫曼树,堆排序 项目流程: (1)统计要压缩的文件中各字符出现的次数,利用数据结构中的小堆来构建Huffman树,每次在堆顶选出两 ...
最新文章
- 当代的设计潮流是什么_当代流行的设计元素
- Visual C++ 编译器选项
- SAP Kyma SSL证书请求文件(CSR)生成逻辑
- 她,既是一个风华绝代的演员,更是WiFi之母...
- 有4个节点可以构造出 二叉树_简单4个步骤就可以拍摄出美丽的城市夜景,赶紧试试吧...
- 时间等待 c# 1614006220
- chromedriver放在哪个目录下_Windows下ThinkPHP与Linux互通
- 内网通不用软件改积分_软件项目为什么不能够如期交付?
- iphone固件降级_手机资讯:降级必备:Phone5如何下载备份SHSH文件
- mysql集群 hbase_hbase完整分布式集群搭建
- 「leetcode」C++题解:20. 有效的括号,括号匹配是使用栈解决的经典问题
- 这70道Java微服务面试题,你能对几道?
- 深入理解浏览器的缓存机制
- 用于测试图片类型限制、图片大小限制的文件
- 【项目实训】微信公众号获取用户openid
- 消除设计教室中的白人至上主义我与设计大师cheryl d miller的对话
- Python实现24点游戏
- Excel制作图表(二)--- 燃尽图
- 上位机发送与接收下位机数据
- 林业工程抗旱造林技术