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

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


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


文件压缩:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/490842.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

兰德公司报告分析 “分布式作战”对美空军兵力运用的影响

来源&#xff1a;兰德公司网站/图片来自互联网导读7月17日&#xff0c;兰德公司发布《竞争环境下的分布式作战&#xff1a;对美国空军兵力运用的影响》的报告&#xff0c;研究了美空军竞争环境下“分布式作战”的能力需求&#xff0c;分析了如何调整美空军当前的“兵力运用模式…

Keras中LSTM的return_sequences和return_state

keras.layers.LSTM()函数有两个重要的参数return_sequences和return_state return_sequences和return_state默认都为false&#xff0c;接下来分别讲解当return_sequences和return_state取不同值时LSTM函数的返回值 return_sequencesTrue会返回每个时间步的隐藏状态&#xff0…

MATLAB(四)在高等数学中的应用

MATLAB在高等数学中的应用 &#xff08;一&#xff09;求极限 matlab中求极限的命令为 limit(expr, x, a) limit(expr, a) limit(expr) limit(expr, x, a, left) limit(expr, x, a, right) 其中&#xff0c;limit(expr, x, a)表示求符号表达式expr关于符号变量x趋近于a时的…

华为手机到底用了多少美国芯?外媒拆解来了

来源&#xff1a;iFixit、IT之家从拆解结果来看&#xff0c;华为Mate 20 X (5G)手机除了使用了3颗美国的芯片产品&#xff08;美光内存、SkyworksWCDMA/LTE低端前端模块、Qorvo中高频段模块&#xff09;以及荷兰公司恩智浦的芯片产品&#xff0c;其余主要部件基本上都是由华为海…

c++判断字符是否为空格或数字

判断字符是否为空格的函数&#xff1a; isspace() 标准的空白字符包括&#xff1a; (0x20) space (SPC) 空格符 \t (0x09) horizontal tab (TAB) 水平制表符 \n (0x0a) newline (LF) 换行符 \v (0x0b) vertical tab (VT) 垂直制表符 \f (0x0c) …

[改善Java代码] 推荐使用序列化实现对象的拷贝

建议44&#xff1a; 推荐使用序列化实现对象的拷贝 上一个建议说了对象的浅拷贝问题&#xff0c;实现Cloneable接口就具备了拷贝能力&#xff0c;那我们来思考这样一个问题&#xff1a;如果一个项目中有大量的对象是通过拷贝生成的&#xff0c;那我们该如何处理&#xff1f;每个…

MATLAB(五)在线性代数中的应用

MATLAB在线性代数中的应用 &#xff08;一&#xff09;向量组的线性相关性 求列向量组A的一个最大线性无关组&#xff0c;可用命令rref(A)将A化成行最简形&#xff0c;其中单位向量对应的列向量即为最大线性无关组所含向量&#xff0c;其他列向量的坐标即为其对应向量用最大线…

C++一位的字符数字转数字

char c4; //1,2,3,.....9 int num0; num int(c - 0);’0’的ASC码是48,‘1’&#xff0c;‘2’......的ASC码分别为49,50...... 和0相减即为对应的数字

关于无穷大量的选择

在解题时候&#xff0c;通常有遇到设置为无穷大的情况。这时候通常用0x7fffffff来设置&#xff0c;他是计算机32位整数最大数&#xff0c;相当于INT_MAX.但是在很多时候这样设置并不会是最佳的&#xff0c;还可能导致bug,这是由于我们有的时候希望无穷大无穷大无穷大&#xff0…

全球市值最大公司的巨变:20年,已是沧海桑田

来源&#xff1a; 资本实验室三十年河东&#xff0c;三十年河西。而商业的世界&#xff0c;变化来得更为迅猛、激烈。就比如当我们比较1999到2019年间的全球市值最大公司&#xff0c;并回看二十年时&#xff0c;惊觉已是沧海桑田。如果以更短的五年为一个观察周期&#xff0c;则…

python+OpenCV图像处理(六)图像平滑与滤波

图像平滑与滤波 运用它&#xff0c;首先就要了解它&#xff0c;什么是平滑滤波&#xff1f; 平滑滤波是低频增强的空间域滤波技术。它的目的有两类&#xff1a;一类是模糊&#xff1b;另一类是消除噪音。空间域的平滑滤波一般采用简单平均法进行&#xff0c;就是求邻近像元点的…

pytorch每次迭代训练前都重新对数据集进行采样形成平衡数据集

对于不平衡数据集的训练通常有两种方法&#xff1a; 一种是先用数据平衡的方法形成平衡数据集之后用于每一轮的训练&#xff0c;此时每轮训练的数据集是不变的&#xff0c;这一方法在pytorch的实现比较简单&#xff0c;即先构建好平衡数据集train_set&#xff0c;然后构建trai…

Delphi中动态调用DLL的方法

Delphi中动态调用dll的方法如下&#xff1a; function CallFunc(dllname, funcname: string; const param: array of const): DWORD; varhLib: THandle;pFunc: Pointer;intSize: Integer; beginResult : 0;hLib : LoadLibrary(PChar(dllname));if hLib <> 0 then beginpF…

语音助手再次敲响隐私保护警钟

来源&#xff1a; 中国科学报智能手机内置的语音助手已成为人们生活中的亲密伙伴&#xff0c;大家习惯于让它完成报时、查询天气等日常任务甚至与它聊天解闷。然而&#xff0c;近期多家科技公司被曝光以智能语音助手录制用户谈话&#xff0c;再通过人工对语音做标注和反馈&…

大数据深度解析NLP文本摘要技术:定义、应用与PyTorch实战

文章目录 大数据深度解析NLP文本摘要技术&#xff1a;定义、应用与PyTorch实战1. 概述1.1 什么是文本摘要&#xff1f;1.2 为什么需要文本摘要&#xff1f; 2. 发展历程2.1 早期技术2.2 统计方法的崛起2.3 深度学习的应用2.4 文本摘要的演变趋势 3. 主要任务3.1 单文档摘要3.2 …

python+OpenCV图像处理(七)图像的形态学处理

图像的形态学处理 数学形态学&#xff08;Mathematical morphology&#xff09;是一门 建立在格论和拓扑学基础之上的图像分析学科&#xff0c;是数学形态学图像处理的基本理论。其基本的运算包括&#xff1a;腐蚀和膨胀、开运算和闭运算、骨架抽取、极限腐蚀、击中击不中变换、…

pandas取某一索引的数据

pandas.DataFrame.take(index) 返回给定位置索引中的元素。注意上面的index是元素的实际位置 举个例子&#xff1a; myList [[a, 10, 1.1],[b, 20, 2.2],[c, 30, 3.3],[d, 40, 4.4]] df1 pd.DataFrame(data myList) print(df1.take([2,3])) 输出&#xff1a; c 30 3.…

站立会议08

一、会议细节 时间   2014-4-27 21&#xff1a;10~21:40 地点   基教102 记录人   王岸城 二&#xff0c;会议记录 姓名今天你做了什么明天你要做什么今天遇到了什么问题王岸城记事本删除记事本修改无法使用数据库 苏月 实现登录和注册的连接 学习数据库 连接数据库信息…

【人工智能】机器人的未来发展:从工业自动化到知识自动化

来源&#xff1a;王飞跃博客【导读】智能时代的核心技术将是知识自动化&#xff0c;因此必须从一开始就加快、加强以软件形态为主的知识机器人的研发与应用&#xff0c;尽快形成软件和物理形态平行互动的新型机器人系统&#xff0c;并以此为突破口&#xff0c;引发下一代智能机…

python+OpenCV图像处理(八)边缘检测

边缘检测 边缘检测是图像处理和计算机视觉中的基本问题&#xff0c;边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。边缘检测是特征提取中的一个研究领域。 图像边缘检测大幅度地减少了数据量&#xff0c;并且剔除了…