基于Huffman算法和LZ77算法的文件压缩(三)

基于Huffman算法和LZ77算法的文件压缩(一)和
基于Huffman算法和LZ77算法的文件压缩(二)讲解Huffman压缩的基本原理和文件压缩的整个过程,接下来讲解文件解压缩的过程

一、 利用huffman编码对源文件进行解压缩

解压缩的整个流程:

  1. 从压缩文件中获取源文件的后缀
  2. 从压缩文件中获取字符次数的总行数
  3. 获取每个字符出现的次数
  4. 重建huffman树
  5. 解压缩

二. 从压缩文件中获取源文件的后缀

如果想要解压缩文件那么就需要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算法的文件解压缩相关推荐

  1. 基于LZ77算法的文件解压缩项目缺陷分析

    基于Huffman算法和LZ77算法的文件压缩(七) 基于Huffman算法和LZ77算法的文件压缩(六)已经讲解完文件压缩的过程,本文讲解文件解压缩的过程和大文件处理方式 一.解压缩的流程 LZ77 ...

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

    一.实现步骤 统计源文件中字符种类和频率 建立Huffman编码树 生成Huffman编码表 压缩文件时,字符匹配编码,将编码写入压缩后文件 解压缩文件时,读取编码,匹配编码表中的字符,写入解压缩后的 ...

  3. 基于LZ77算法的文件压缩铺垫

    基于Huffman算法和LZ77算法的文件压缩(四) 本文开始讲解LZ77算法,会用到哈希,哈希原理详解 我们在基于Huffman算法和LZ77算法的文件压缩(一)当中总体介绍了Huffman算法和L ...

  4. huffman算法实现文件的压缩与解压

            本文采用哈夫曼编码的方式进行文件的压缩和解压缩,主要原理是通过huffman编码来表示字符,出现次数多的编码短,出现次数少的编码长,这样整体而言,所需的总的bit位是减少的.但是当大部 ...

  5. 单向散列函数概述并基于MD5算法对文件哈希值实时监测

    1.如何验证文件是否被修改过 只生成一个指纹文件,对指纹文件进行验证 当已经存储的文件被修改之后,指纹文件就会跟着变化,即生成一个单向散列函数 任意长度的数据都对应固定长度的散列值–减少匹配开销 散列 ...

  6. 基于LZ77算法的文件压缩收尾

    基于Huffman算法和LZ77算法的文件压缩(六) 前面基于Huffman算法和LZ77算法的文件压缩(四) 基于Huffman算法和LZ77算法的文件压缩(五) 已经充分讲解LZ77到基本原理和实 ...

  7. 基于LZ77算法的文件压缩

    基于Huffman算法和LZ77算法的文件压缩(五) 基于Huffman算法和LZ77算法的文件压缩(四)已经讲解LZ77算法到基本原理和压缩过程. 本文详细讲解文件压缩过程当中的问题 一.文件压缩的 ...

  8. BUAA数据结构作业——基于Huffman码的文件压缩工具

    目录 前言 题目 问题描述 输入形式 输出形式 样例 问题分析 代码实现 前言 大家好哇!今天给大家分享一道Huffman码的实战应用题.Huffman码来源于Huffman树,假设二叉树有m个叶结点 ...

  9. 基于Huffman树的文件压缩(详细)

    文件压缩 开发平台:Visual Studio 2008 开发技术:哈夫曼树,堆排序 项目流程: (1)统计要压缩的文件中各字符出现的次数,利用数据结构中的小堆来构建Huffman树,每次在堆顶选出两 ...

最新文章

  1. 当代的设计潮流是什么_当代流行的设计元素
  2. Visual C++ 编译器选项
  3. SAP Kyma SSL证书请求文件(CSR)生成逻辑
  4. 她,既是一个风华绝代的演员,更是WiFi之母...
  5. 有4个节点可以构造出 二叉树_简单4个步骤就可以拍摄出美丽的城市夜景,赶紧试试吧...
  6. 时间等待 c# 1614006220
  7. chromedriver放在哪个目录下_Windows下ThinkPHP与Linux互通
  8. 内网通不用软件改积分_软件项目为什么不能够如期交付?
  9. iphone固件降级_手机资讯:降级必备:Phone5如何下载备份SHSH文件
  10. mysql集群 hbase_hbase完整分布式集群搭建
  11. 「leetcode」C++题解:20. 有效的括号,括号匹配是使用栈解决的经典问题
  12. 这70道Java微服务面试题,你能对几道?
  13. 深入理解浏览器的缓存机制
  14. 用于测试图片类型限制、图片大小限制的文件
  15. 【项目实训】微信公众号获取用户openid
  16. 消除设计教室中的白人至上主义我与设计大师cheryl d miller的对话
  17. Python实现24点游戏
  18. Excel制作图表(二)--- 燃尽图
  19. 上位机发送与接收下位机数据
  20. 林业工程抗旱造林技术

热门文章

  1. chromium浏览器_微软将全面向Windows 10用户推送Chromium版Edge浏览器
  2. 共模干扰和差模干扰(图解)---摘自: 硬件十万个为什么
  3. 直流无刷电机制动的三种方式
  4. CSS中盒模型的理解
  5. jsp页面模块的来源
  6. CSS中的position定位
  7. mybatis中条件表达式if的test为字符串时值比较
  8. Unity3D如何有效地组织代码?(转)
  9. 【Go语言】【2】Sublime配置GO开发环境
  10. hibernate JPA 双向多对多   bi-directional many-to-many association