这几天在较为认真的研究基于哈夫曼编码的文件压缩及解压,费了点时间,在这分享一下:

这里用链式结构,非顺序表结构;

文件压缩:

1.获取文件信息(这里采用TXT格式文本);

2.压缩文件;

3.写配置文件(便于解压时用,无非就是存放原文件的索引之类的,比如说,文件中某个字符出现的个数,记录下来)

4.解压缩,使用压缩后的文件和配置文件解压文件;

5.用比对软件,比对解压后的文件和源文件是否相同;

下面慢慢解析:

先看一个文件信息类:

typedef long long LongType;
struct FileInfo
{unsigned char _ch;       //字符LongType _count;         //字符出现次数string _code;            //字符对应的哈夫曼编码 FileInfo(unsigned char ch = 0):_ch(ch),_count(0){}FileInfo operator+(const FileInfo& x){FileInfo tmp;tmp._count = this->_count + x._count;return tmp;}bool operator !=(const FileInfo& x) const{return this->_count != x._count;}
};bool operator<(const FileInfo info1,const FileInfo info2)
{return info1._count < info2._count;
}

此为一个文件信息的类结构,包含字符,字符对应出现的次数,以及这个字符对应的哈夫曼编码(能看到这篇博客的星弟,对哈夫曼编码不会陌生,这里不再强调)

除了统计字符出现的次数及哈夫曼编码,还完成了几个运算符的重载

要获取哈夫曼编码,就得建立哈夫曼树,建立哈夫曼树用最小堆取操作,以下是最小堆建立过程

// 小堆
template<class T>
struct Less
{bool operator() (const T& l, const T& r){return l < r; // operator<}};template<class T>
struct Greater
{bool operator() (const T& l, const T& r){return l > r; // operator<}
};template<class T, class Compare = Less<T>>
class Heap
{
public:Heap(){}Heap(const T* a, size_t size){for (size_t i = 0; i < size; ++i){_arrays.push_back(a[i]);}// 建堆for(int i = (_arrays.size()-2)/2; i >= 0; --i){AdjustDown(i);}}void Push(const T& x){_arrays.push_back(x);AdjustUp(_arrays.size()-1);}void Pop(){assert(_arrays.size() > 0);swap(_arrays[0], _arrays[_arrays.size() - 1]);_arrays.pop_back();AdjustDown(0);}T& Top(){assert(_arrays.size() > 0);return _arrays[0];}bool Empty(){return _arrays.empty();}int Size(){return _arrays.size();}void AdjustDown(int root){int child = root*2 + 1;//   Compare com;while (child < _arrays.size()){// 比较出左右孩子中小的那个if (child+1<_arrays.size() &&*_arrays[child+1] < _arrays[child])//if(child+1<_arrays.size() &&//   com(_arrays[child+1],_arrays[child])){++child;}if(*_arrays[child] < _arrays[root])//if(com(_arrays[child],_arrays[root])){swap(_arrays[child], _arrays[root]);root = child;child = 2*root+1;}else{break;}}}void AdjustUp(int child){int parent = (child-1)/2;//while (parent >= 0)while (child > 0){if (*_arrays[child] < _arrays[parent]){swap(_arrays[parent], _arrays[child]);child = parent;parent = (child-1)/2;}else{break;}}}public:vector<T> _arrays;
};

最小堆里也完成了很多接口,包括push  pop等

然后就是几个压缩和解压的函数接口

1.根据哈夫曼树获取哈夫曼变慢:

 void _GenerateHuffmanCode(HuffmanTreeNode<FileInfo>* root){if (root == nullptr){return;}_GenerateHuffmanCode(root->_left);_GenerateHuffmanCode(root->_right);//当前节点为叶子节点为空  才生成哈夫曼编码if (root->_left == nullptr && root->_right == nullptr){HuffmanTreeNode<FileInfo>* cur = root;HuffmanTreeNode<FileInfo>* parent = cur->_parent;string& code = _infos[cur->_weight._ch]._code;while (parent){if (parent->_left == cur){code += '1';}else if (parent->_right == cur){code += '0';}cur = parent;parent = cur->_parent;}reverse(code.begin(), code.end());}}

2.根据最小堆建立哈夫曼树;

void CreateTree(T *a, size_t size, const T& invalid){assert(a);Heap<HuffmanTreeNode<T>*> s1;  //草 终于发现问题  在这里   (堆里放的是指针,类型一定要对)//找两个最小的元素for (size_t i = 0; i < size; ++i){if (a[i] != invalid){HuffmanTreeNode<T>* node = new HuffmanTreeNode<T>(a[i]);s1.Push(node);}}while (s1.Size() > 1){HuffmanTreeNode<T>* left = s1.Top();s1.Pop();HuffmanTreeNode<T>* right = s1.Top();s1.Pop();HuffmanTreeNode<T>* parent = new HuffmanTreeNode<T>(left->_weight + right->_weight);parent->_left = left;parent->_right = right;left->_parent = parent;right->_parent = parent;s1.Push(parent);}_root = s1.Top();s1.Pop();}

3.读取文本文件中的一行:

  bool _ReadLine(FILE *fOutLogFile, string& line){char ch = fgetc(fOutLogFile);if (feof(fOutLogFile))return false;else{if (ch == '\n'){line += ch;ch = fgetc(fOutLogFile);}while (ch != '\n'){line += ch;ch = fgetc(fOutLogFile);}return true;}}

4.文件压缩

   //文件压缩bool Compress(const char* filename){//1.打开一个文件,统计文件字符出现的次数//2.生成对应的哈弗曼编码//3.压缩文件//4.写配置文件,方便解压缩assert(filename);FILE *fOut = fopen(filename, "rb");assert(fOut);//统计文件字符出现的次数unsigned char ch = fgetc(fOut);while (!feof(fOut))  //文件结束{_infos[ch]._count++;ch = fgetc(fOut);}HuffmanTree<FileInfo> ht;FileInfo invalid;ht.CreateTree(_infos, 256, invalid);//哈夫曼编码_GenerateHuffmanCode(ht.GetRoot());string compressFile = filename;compressFile += ".huf";//压缩后的文件名 后缀为《输入文件名+.huf》FILE *finCompress = fopen(compressFile.c_str(), "wb"); //获取string中的C字符串assert(finCompress);fseek(fOut, 0, SEEK_SET);//将文件指针移到开头char cha = fgetc(fOut);unsigned char inch = 0;int index = 0;  //一个字节的八位while (!feof(fOut)){string& code = _infos[(unsigned char)cha]._code;for (size_t i = 0; i < code.size(); ++i){inch <<= 1;     //低位向高位进if (code[i] == '1'){inch |= 1;}if (++index == 8){fputc(inch, finCompress); //够8位,装进文件index = 0;   //重新一轮开始inch = 0;}}cha = fgetc(fOut);}fclose(fOut);//如果index = 0 说明 上边8位刚好存满 不等 下一个自己又出来了if (index != 0)   //处理最后一个字符不够的问题{inch <<= (8 - index); //最高位必须装上 后边的浪费掉fputc(inch, finCompress);}fclose(finCompress);}

5.写配置文件:

string logFile = filename;logFile += ".log";FILE *Log = fopen(logFile.c_str(), "wb");assert(Log);string chInfo;char str[128] = {0}; //没空间 不可以for (size_t i = 1; i < 256; ++i){if (_infos[i]._count > 0){chInfo += _infos[i]._ch;chInfo += ',';chInfo += _itoa(_infos[i]._count,str,10);chInfo += '\n';fputs(chInfo.c_str(), Log);chInfo.clear();}}fclose(Log);

6.最后的文件解压:

//重构文件void _RestoreFiles(HuffmanTreeNode<FileInfo> *root, const char* Fileneme,long long size){assert(root);//原压缩文件string name = Fileneme;name += ".huf";FILE* Out = fopen(name.c_str(),"rb");assert(Out);string restorefilename = Fileneme;restorefilename += ".over";FILE *over = fopen(restorefilename.c_str(),"wb");assert(over);int pos = 8;long long poss = size;unsigned char chz = fgetc(Out);while (poss>0){HuffmanTreeNode<FileInfo>* cur = nullptr;cur = root;while (cur->_left != nullptr || cur->_right != nullptr){pos--;unsigned char temp = chz >> pos;int ch = 1 & temp;if (ch == 0){cur = cur->_right;}else if (ch == 1){cur = cur->_left;}if (pos == 0){chz = fgetc(Out);pos = 8;}}fputc(cur->_weight._ch, over);poss--;}fclose(Out);fclose(over);}void UnCompress(const char* Fileneme)//解压缩{//1.打开日志文件//2.根据信息还原哈夫曼树//3.还原信息;string UnCompressneme = Fileneme;UnCompressneme += ".log";FILE *fOutLogFile = fopen(UnCompressneme.c_str(), "rb");assert(fOutLogFile);string line;while (_ReadLine(fOutLogFile, line)){unsigned char ch = line[0];_infos[ch]._count = atoi(line.substr(2).c_str());line.clear();} HuffmanTree<FileInfo> f;FileInfo invalid;f.CreateTree(_infos, 256, invalid);//根据重建的哈夫曼树 还原文件;long long size = f.GetRoot()->_weight._count;_RestoreFiles(f.GetRoot(), Fileneme,size);}

到此,此项目基本完成;如遇问题,希望留言,随时解答,如有见解,跪求赐教!

转载于:https://www.cnblogs.com/li-ning/p/9490022.html

基于哈夫曼编码完成的文件压缩及解压相关推荐

  1. 哈夫曼字符串编码c语言实现,基于哈夫曼(haffuman)算法的文件压缩的实现(C语言)(原创)...

    本文首先简要阐述哈夫曼算法的基本思想,然后介绍了使用哈夫曼算法进行文件压缩和解压缩的 处理步骤,最后给出了C语言实现的文件压缩和解压缩的源代码. 哈夫曼算法的主要思想是: ①首先遍历要处理的字符串,得 ...

  2. 基于哈夫曼编码对文件进行压缩和解压缩(详细讲解)

    基于哈夫曼编码对文件进行压缩和解压缩(详细讲解) 本文对应c++代码实现链接 一.背景 利用特定的算法来压缩数据的工具,压缩后生成的文件称为压缩包.如果想使用其中的数据,就得用压缩软件对数据进行解压. ...

  3. 哈夫曼编码及文本文件的压缩解压(c++SourceCode)

    哈夫曼编码是一种编码方式,是可变字长编码(VLC)的一种.以哈夫曼树-即最优二叉树,带权路径长度最小的二叉树,经常应用于数据 压缩. 在计算机信息处理中,"哈夫曼编码"是一种一致性 ...

  4. 利用huffman编码对文本文件进行压缩与解压(java实现)

    利用huffman编码对文本文件进行压缩与解压 输入:一个文本文件 输出:压缩后的文件 算法过程: (1)统计文本文件中每个字符的使用频度 (2)构造huffman编码 (3)以二进制流形式压缩文件 ...

  5. c# 文件压缩、解压及下载

    C#打包文件夹成zip格式(包括文件夹和子文件夹下的所有文件) C# 文件压缩与解压(ZIP格式) asp.net实现文件夹及文件压缩,并实现下载 转载于:https://www.cnblogs.co ...

  6. 7z001怎么解压在安卓手机上面_安卓zip文件压缩RAR解压手机下载-安卓zip文件压缩RAR解压v1.0最新版下载...

    安卓zip文件压缩RAR解压是一款非常好用的手机压缩解压缩神器,在安卓zip文件压缩RAR解压上我们可以看到很多的实用的功能,软件可以帮助我们更好的处理我们手机中的文件,感兴趣的朋友赶紧下载安卓zip ...

  7. java 操作Zip文件(压缩、解压、加密)

    java 操作Zip文件(压缩.解压.加密) 依赖:点击下载 package com.zxl.test;import net.lingala.zip4j.model.ZipParameters; im ...

  8. 7z文件压缩、解压 (7zTool.exe)

    工具下载 压缩为7z: 调用zip()函数 7z解压缩: 调用unzip()函数 using System; using System.Collections.Generic; using Syste ...

  9. linux把一个大文件压缩,linux大文件压缩及解压需要注意问题

    注意: 大文件压缩及解压需要在后台进行,如果要查看解压详情,就要输出重定向. 远程服务器,要防止网络断开连接,导致终端关闭,此时终端断开,即使后台进行,解压以及压缩也会停止.解决方法:在指令前加noh ...

最新文章

  1. Typescript之 范型
  2. 3分钟掌握支持向量机-机器学习面试必备
  3. JAVA线程池管理及分布式HADOOP调度框架搭建
  4. java压缩----使用ANT JDK压缩---解决中文问题
  5. python变量定义大全_详解python变量与数据类型
  6. Spring Data JPA 从入门到精通~EntityManager介绍
  7. 多IDC GSLB的部署
  8. android 方法统计,Android 利用编译时 注入 统计App内所有方法执行时常,分析ANR
  9. mysql8连接java_JAVA连接MYSQL8.0问题
  10. php留言板上传图片,thinkphp3.2.3留言板带管理没有后台ajax上传图片功能
  11. 【转】C# 控件的自定义拖动、改变大小方法
  12. DBeaver - 一款免费开源的通用数据库工具
  13. 【VSCode】SSH远程连接服务器
  14. 关于人工智能研究思路的一点设想
  15. 【致青春】奋斗迷茫的我们
  16. 基于归一化互相关函数的语音基音周期检测
  17. JavaScript聊天器
  18. 使用case when,union all实现sql行转列、列转行
  19. 02 ,概率论 :初级概念,极差,频率,直方图,曲线图
  20. 【观察】数据中心从制冷到“智冷”,华为践行“双碳”更进一步

热门文章

  1. 机械厂html5手机模板,营销型机械消费设备企业通用织梦模板(带html5手机端) v1.0...
  2. 升级 ubuntu_Ubuntu 19.04 已经到期!现有用户必须升级到 Ubuntu 19.10
  3. LeetCode 785. 判断二分图(染色法)
  4. LeetCode 422. 有效的单词方块
  5. LeetCode 468. 验证IP地址
  6. LeetCode 658. 找到 K 个最接近的元素(二分查找)
  7. postek二次开发_20190626_二次开发BarTender打印机_C#代码_一边读取TID_一边打印_打印机POSTEK...
  8. cpu使用率_漫话性能:CPU使用率
  9. php7 关联nginx,nginx+php7配合开发
  10. 87说明书 ikbc_女性玩家的首选!——IKBC白无垢. 樱机械键盘赏评