Huffman编码对英文文本的压缩和解压缩
英文文件的压缩和解压缩程序
合肥学院计算机科学与技术系一、问题分析和任务定义1、题目采用哈夫曼编码思想实现英文文本的压缩与解压缩,并提供压缩前后的占用空间比。
要求 1)压缩原文件规模不小于5K2) 提供解压缩文件后文件与原文件的相同性比较功能2、问题分析压缩过程 news.txt →news1.txt1、以读的形式打开需要压缩的一个英文本文件,把其中出现的所有字符按其在文本中出现的频率利用哈夫曼树进行编码。
2、以写的形式打开一个新的文本文件,把它作为英文文本压缩后的文本文件,扫描需要压缩的英英文本文件( new.txt )中所有字符,把其对应的编码通过转换后存入新的文本文件( new1.txt )中。
3、把需要压缩的英文文本( new.txt )中所出现的字符及其编码等原始文件的信息保存在新的文本文件中。
解压缩过程news1.txt →news2.txt1、以读的形式打开一个压缩文件 news1.txt,按其中保存的原始文件的信息还原哈夫曼树及字符编码。
2、以写的形式打开一个新的文本文件,作为解压后的英文文本 news2.txt ,逐个扫描压缩文件 news1.txt中的所有字符,把其中所有转换后的编码再转换回来并与哈夫曼树中存储的字符编码比较,把其对应的字符写入news2.txt 中。
一个字符在文本文件中存储时占一个字节,而其二进制编码若直接存入文本文件其所占的空间不会少于一个字节。
例如:假设字符E的编码为001,若把001直接存入文件只能用字符串的形式,其所占用的空间为三个字节。
达不到文件压缩的目的,所以必须对编码的存储空间进行转换。
3 编码转换在文本文件中字符之间是连续的,所以在文本文件中存储编码也是连续的。
可以把连续的不同字符的编码存入同一个字节,再把这一个字节的二进制码转换成一个字符,把转换后的字符存储在文本文件中。
二、数据结构的选择和概要设计:1 此程序采用的数据结构为顺序表。
哈夫曼树是二叉树的一种,二叉树的顺序存储结构中可以把结点间的关系放在其存储位置中,无需附加任何信息就能在这种结构中找到每个结点的双亲结点和孩子结点,这正是哈夫曼编码所需要的。
Huffman的应用之文件压缩与解压缩
Huffman的应用之文件压缩与解压缩最近这段时间一直在学习树的这种数据结构,也接触到了Huffman树以及了解了什仫是Huffman编码,而我们常用的zip压缩也是利用的Huffman编码的特性,那仫是不是可以自己实现一个文件压缩呢?当然可以了.在文件压缩中我实现了Huffman树和建堆Heap的代码,zip压缩的介绍>下面开始介绍自己实现的文件压缩的思路和问题...1).统计>读取一个文件统计这个文件中字符出现的次数.2).建树>以字符出现的次数作为权值使用贪心算法构建Huffman树(根据Huffman树的特性>字符出现次数多的一定靠近根结点,出现次数少的一定远离根结点).3).生成Huffman编码>规则左0右1.4).压缩>再次读取文件,根据生成的Huffman编码压缩文件.5).生成配置文件>将字符以及字符出现的次数写进配置文件中.6).解压缩>利用配置文件还原出Huffman树,根据压缩文件还原出原文件.7).测试>判断解压是否正确需要判断原文件和解压缩之后的文件是否相同,利用Beyond Compare软件进行对比.下面是我举的一个简单的范例,模拟压缩和解压缩的过程,希望有读者有帮助利用Beyond Compare软件进行对比>在实现中出现了很多的问题,下面我提出几个容易犯的问题,仅供参考1).在使用贪心算法构建Huffman树的时候,如果我们以unsigned char一个字节来存储它总共有2^8=256个字符,如果将所有的字符都构建Huffman树,这不仅降低了效率还将分配极大的内存.所以我设立了非法值这个概念,只有当字符出现的次数不为0的时候才将该字符构建到Huffman树中.2).在写压缩文件的时候我们是将字符对应的Huffman编码转化为对应的位,每当填满一个字节(8位)后再写入压缩文件中.如果最后一个字节没有填满我们就将已经填的位移位空出后几个位置,将未满的位置补0补满一个字节再写入压缩文件中.3).如果我们将源文件压缩之后再去解压,因为你的Huffman树和Huffman编码都是在压缩函数中得到的,此时再去解压那仫你的Huffman编码以及不存在了该如何去还原文件呢?这就是为什仫要生成配置文件的原因了,在配置文件中写入了字符以及字符出现的次数.在解压缩中根据配置文件构建新的Huffman树.4).由压缩文件还原文件的时候如何知道压了多少个字符呢?也就是说因为我们在压缩的时候最后一个字节是补了0的在解压缩的时候可能会把这个补的位当成字符的编码来处理.一种想法是在统计字符出现的次数的时候设置一个变量,每读取一个字符该变量就加1,最后将该变量写进配置文件中.另外一种想法就是根据根结点的权值,通过上述简单的实例观察可知根结点权值中的_count就是字符出现的次数.解决了以上几个问题,我的程序已经可以压缩那256个字符并正确的还原了,那仫如果是大文件或者是汉字,图片以及音频视频呢?1).因为有些特殊的字符编码,所以我们统计字符出现的次数的时候应该用的是unsigned char,刚开始我用的文件结束标志是EOF在ASII中它的编码是-1此时已经不可以用EOF来判断是否文件结束了,所以我用了feof这个函数来判断文件是否结束.2).统计字符出现次数应该用的类型是long long,这就解决了大文件的压缩和解压缩的问题了.3).因为汉字,图片,视频这些在内存中是以二进制的形式存在的,所以我们将以文本形式打开读或者写的修改为以二进制的形式读或者写.为了验证大文件的压缩我找了一个8.09M的文件压缩之后是6.50M,并且可以正确还原.1).测试效率>2).利用Beyond Compare软件进行对比,如果一样说明压缩成功>[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片#define _CRT_SECURE_NO_W ARNINGS 1#pragma once#include "Heap.h"template<class T>struct HuffmanTreeNode{T _weight;HuffmanTreeNode<T> *_left;HuffmanTreeNode<T> *_right;HuffmanTreeNode<T> *_parent;HuffmanTreeNode(const T& w=T()):_weight(w),_left(NULL),_right(NULL),_parent(NULL){}};template<class T>class HuffmanTree{typedef HuffmanTreeNode<T> Node;public:HuffmanTree():_root(NULL){}HuffmanTree(const T* a,size_t size):_root(NULL){_root=_CreatHuffmanTree(a,size);}//将未出现的字符过滤出来,不构造堆HuffmanTree(const T* a,size_t size,const T& invalid){_root=_CreatHuffmanTree(a,size,invalid);}Node* GetRoot(){return _root;}~HuffmanTree(){_Destroy(_root);}protected:Node *_CreatHuffmanTree(const T* a,size_t size){struct NodeLess{bool operator()(Node *l,Node *r)const{return l->_weight < r->_weight;}};Heap<Node *,NodeLess> minHeap;//建立结点并放入vector中for (size_t i=0;i<size;++i){Node *tmp=new Node(a[i]);minHeap.Push(tmp);}//取出较小的两个结点作为左右孩子并构建父结点while (minHeap.Size() > 1){Node *left=minHeap.Top();minHeap.Pop();Node *right=minHeap.Top();minHeap.Pop();Node *parent=new Node(left->_weight + right->_weight);parent->_left=left;parent->_right=right;left->_parent=parent;right->_parent=parent;minHeap.Push(parent);}return minHeap.Top();}//思路类似不带过滤功能的Node *_CreatHuffmanTree(const T* a,size_t size,const T& invalid) {struct NodeLess{bool operator()(Node *l,Node *r)const{return l->_weight < r->_weight;}};Heap<Node *,NodeLess> minHeap;//建立结点并放入vector中for (size_t i=0;i<size;++i){if(a[i] != invalid){Node *tmp=new Node(a[i]);minHeap.Push(tmp);}}//取出较小的两个结点作为左右孩子并构建父结点while (minHeap.Size() > 1){Node *left=minHeap.Top();minHeap.Pop();Node *right=minHeap.Top();minHeap.Pop();Node *parent=new Node(left->_weight + right->_weight);parent->_left=left;parent->_right=right;left->_parent=parent;right->_parent=parent;minHeap.Push(parent);}return minHeap.Top();}void _Destroy(Node *&root)if(root == NULL)return ;Node *cur=root;if(cur){_Destroy(cur->_left);_Destroy(cur->_right);delete cur;cur=NULL;return ;}}protected:Node *_root;};void testHuffmanTree(){int a[]={0,1,2,3,4,5,6,7,8,9};int size=sizeof(a)/sizeof(a[0]);HuffmanTree<int> ht(a,size);}[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片#define _CRT_SECURE_NO_W ARNINGS 1#pragma once//利用仿函数的特性实现代码的复用性template<class T>struct Small{bool operator()(const T& l,const T& r){return l < r;}};template<class T>struct Large{bool operator()(const T& l,const T& r)return l > r;}};template<class T,class Compare=Large<T>> //缺省是建小堆class Heap{public:Heap(){}Heap(const T *a,int size){assert(a);_a.reserve(size);for (int i=0;i<size;++i){_a.push_back(a[i]);}//建堆的时候从倒数第一个非叶子结点开始.for (int j=(size-2)/2;j>=0;--j){_AdjustDown(j);}}void Push(const T& x){_a.push_back(x);_AdjustUp(_a.size()-1);}void Pop(){assert(!_a.empty());swap(_a[0],_a[_a.size()-1]);_a.pop_back();_AdjustDown(0);}size_t Size(){return _a.size();}bool Empty(){return _a.empty();const T& Top()const{assert(!_a.empty());return _a[0];}void Display(){for (size_t i=0;i<_a.size();++i){cout<<_a[i]<<" ";}cout<<endl;}protected:void _AdjustDown(int root){int parent=root;size_t child=2*root+1;while (child < _a.size()){Compare com;//child指向左右孩子中较大的那个数//if (child+1 < _a.size()// && _a[child+1] > _a[child])if(child+1 < _a.size()&& com(_a[child+1],_a[child])){child++;}//if (_a[child] > _a[parent])if(com(_a[child],_a[parent])){swap(_a[child],_a[parent]);parent=child;//初始的child默认指向左孩子child=2*parent+1;}elsebreak;}}void _AdjustUp(int child){while (child > 0){int parent=(child-1)/2;Compare com;//if (_a[child] > _a[parent])if(com(_a[child],_a[parent])){swap(_a[child],_a[parent]);child=parent;}else//插入的数据比父节点的数据域小break;}}protected:vector<T> _a;};//利用堆解决优先级队列的问题template<class T,class Compare=Large<T>>class PriorityQueue{public:PriorityQueue(int *a,int size):_pq(a,size){}void Push(const T& x){_pq.Push(x);}void Pop(){_pq.Pop();}const T& Top()const{return _pq.Top();}void Display(){_pq.Display();}protected:Heap<T,Compare> _pq;};[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片#define _CRT_SECURE_NO_W ARNINGS 1#pragma once#include "HuffmanTree.h"typedef long long Type;struct CharInfo{unsigned char _ch; //出现的字符Type _count; //统计次数string _code; //Huffman编码CharInfo(Type count=0):_ch(0),_count(count),_code(""){}//重载对应的操作符CharInfo operator + (const CharInfo& fc)const{return CharInfo(_count + fc._count);}bool operator != (const CharInfo fc)const{return _count != fc._count;}bool operator < (const CharInfo& fc)const{return _count < fc._count;}};class FileCompress{public://默认的构造函数FileCompress(){for(size_t i=0;i<256;++i){_infos[i]._ch=i;}}string Compress(const char *filename){assert(filename);FILE *pf=fopen(filename,"rb");assert(pf);unsigned char ch=fgetc(pf);//统计字符出现的次数while (!feof(pf)){_infos[ch]._count++;ch=fgetc(pf);}//以该字符出现的次数构建一颗HuffmanTree.CharInfo invalid; //非法值HuffmanTree<CharInfo> ht(_infos,256,invalid);//生成Huffman编码string code;_CreatHuffmanCode(ht.GetRoot(),code);//_CreatHuffmanCode(ht.GetRoot());//压缩文件fseek(pf,0,SEEK_SET); //回到文件头string compressfile=filename;compressfile += ".compress"; //压缩后的文件名FILE *fin=fopen(compressfile.c_str(),"wb");assert(fin);size_t pos=0; //记录位数unsigned char value=0;ch=fgetc(pf);while (!feof(pf)){string &code=_infos[ch]._code;for (size_t i=0;i<code.size();++i){value <<= 1;if(code[i] == '1')value |= 1;elsevalue |= 0; //do-nothing++pos;if(pos == 8) //满一个字节{fputc(value,fin);value=0;pos=0;}}ch=fgetc(pf);}if(pos) //解决不足8位的情况.{value <<= (8-pos);fputc(value,fin);}//配置文件--便于重建Huffman树string ename=filename;configfilename += ".config";FILE *finconfig=fopen(configfilename.c_str(),"wb");assert(finconfig);string line;char buff[128];for (size_t i=0;i<256;++i){//一行一行的读if (_infos[i]._count){line += _infos[i]._ch;line += ",";line += _itoa(_infos[i]._count,buff,10);line += "\n";//fputs(line.c_str(),finconfig);fwrite(line.c_str(),1,line.size(),finconfig);line.clear();}}fclose(pf);fclose(fin);fclose(finconfig);return compressfile;}string UnCompress(const char *filename){assert(filename);string configfilename=filename;size_t index=configfilename.rfind(".");configfilename=configfilename.substr(0,index); configfilename += ".config";FILE *foutconfig=fopen(configfilename.c_str(),"rb"); assert(foutconfig);string line;//读取配置文件--获取字符出现的次数unsigned char ch=0;while (ReadLine(foutconfig,line)){if(line.empty()){line += '\n';continue;}//读到空行ch=line[0];_infos[ch]._count = atoi(line.substr(2).c_str());line.clear();}//构建Huffman树CharInfo invalid;HuffmanTree<CharInfo> hft(_infos,256,invalid);//根结点的权值也就是字符出现的次数总和HuffmanTreeNode<CharInfo> *root=hft.GetRoot(); Type charcount=root->_weight._count;//解压缩string uncompressfilename=filename;index=uncompressfilename.rfind("."); uncompressfilename=uncompressfilename.substr(0,index); uncompressfilename += ".uncompress";FILE *fin=fopen(uncompressfilename.c_str(),"wb"); assert(fin);//由压缩文件还原文件string compressfilename=filename;FILE *fout=fopen(compressfilename.c_str(),"rb");assert(fout);HuffmanTreeNode<CharInfo> *cur=root;int pos=7;ch=fgetc(fout);while (charcount > 0){while (cur){if(cur->_left == NULL && cur->_right == NULL){//叶子结点fputc(cur->_weight._ch,fin);cur=root;--charcount;if (charcount == 0) //所有的字符都处理完成break;}if (ch & (1 << pos)) //检查字符的每个位cur=cur->_right; //1向右走elsecur=cur->_left; //0向左走--pos;if(pos < 0) //一个字节解压完成{ch=fgetc(fout);pos=7;}}}fclose(foutconfig);fclose(fin);fclose(fout);return uncompressfilename;}//读取一行字符并放在line中bool ReadLine(FILE *fout,string& line){int ch=fgetc(fout);if(ch == EOF)return false;while (ch != EOF && ch != '\n'){line += ch;ch=fgetc(fout);}return true;}protected://递归的方法求HuffmanTreeCodevoid _CreatHuffmanCode(HuffmanTreeNode<CharInfo> *root,string &code) {if(root == NULL)return ;_CreatHuffmanCode(root->_left,code+'0');_CreatHuffmanCode(root->_right,code+'1');if(root->_left == NULL && root->_right == NULL) //叶子结点{_infos[root->_weight._ch]._code=code;}}//非递归求HuffmanTreeCodevoid _CreatHuffmanCode(HuffmanTreeNode<CharInfo> *root){if(root == NULL)return ;_CreatHuffmanCode(root->_left);_CreatHuffmanCode(root->_right);if(root->_left == NULL && root->_right == NULL) //叶子结点{string& code=_infos[root->_weight._ch]._code;HuffmanTreeNode<CharInfo> *cur=root;HuffmanTreeNode<CharInfo> *parent=root->_parent;while (parent){if(parent->_left == cur)code.push_back('0'); //左0elsecode.push_back('1'); //右1cur=parent;parent=cur->_parent;}//编码是从根到叶子结点的,需要逆置reverse(code.begin(),code.end());}}protected:CharInfo _infos[256];};void testFileCompress(){FileCompress fc;cout<<"开始压缩"<<endl;cout<<"压缩用时:";int start=GetTickCount();press("2.png"); //input input.BIG 3.mp3int end=GetTickCount();cout<<end-start<<endl;cout<<"开始解压"<<endl;cout<<"解缩用时:";start=GetTickCount();fc.UnCompress("press"); //press press 3.mp3end=GetTickCount();cout<<end-start<<endl;}void testFileUncompress(){FileCompress fc;cout<<"开始解压"<<endl;cout<<"解缩用时:";int start=GetTickCount();fc.UnCompress("2.png");int end=GetTickCount();cout<<end-start<<endl;}经过测试这个小项目已经可以压缩并还原一些文件了,目前还没有发现什仫大的Bug,如果有童鞋发现请第一时间告诉我哦...。
huffman编码译码实现文件的压缩与解压.
数据结构课程设计题目名称:huffman编码与解码实现文件的压缩与解压专业年级:组长:小组成员:指导教师:二〇一二年十二月二十六日目录一、目标任务与问题分析 (2)1.1目标任务 (2)1.2问题分析 (2)二、算法分析 (2)2.1构造huffman树 (2)2.1.1 字符的统计 (2)2.1.2 huffman树节点的设计 (2)2.2构造huffman编码 (3)2.2.1 huffman编码的设计 (3)2.3 压缩文件与解压文件的实现 (3)三、执行效果 (4)3.1界面 (4)3.2每个字符的编码 (4)3.3操作部分 (5)3.4文件效果 (6)四、源程序 (7)五、参考文献 (16)huffman编码与解码实现文件的压缩与解压一、目标任务与问题分析1.1目标任务采用huffman编码思想实现文件的压缩和解压功能,可以将任意文件压缩,压缩后也可以解压出来。
这样即节约了存储空间,也不会破坏文件的完整性。
1.2问题分析本问题首先应该是利用哈夫曼思想,对需要压缩的文件中的个字符进行频率统计,为了能对任意的文件进行处理,应该所有的文件以二进制的方式进行处理,即对文件(不管包含的是字母还是汉字)采取一个个的字节处理,然后根据统计的频率结果构造哈夫曼树,然后对每个字符进行哈夫曼编码,然后逐一对被压缩的文件的每个字符构建的新的哈夫曼编码存入新的文件中即得到的压缩文件。
解压过程则利用相应的哈夫曼树及压缩文件中的二进制码将编码序列译码,对文件进行解压,得到解压文件。
二、算法分析2.1构造huffman树要利用哈夫曼编码对文本文件进行压缩,首先必须知道期字符相应的哈夫曼编码。
为了得到文件中字符的频率,一般的做法是扫描整个文本进行统计,编写程序统计文件中各个字符出现的频率。
由于一个字符的范围在[0-255]之间,即共256个状态,所以可以直接用256个哈夫曼树节点即数组(后面有节点的定义)空间来存储整个文件的信息,节点中包括对应字符信息,其中包括频率。
训练2+用Huffman编码实现文本文件的无损压缩
用Huffman编码实现文件压缩算法设计训练(2)2011-3-21基本要求•目标–独立完成一个中等规模的项目–在程序设计中使用中等复杂度的数据结构•基本要求–自行建立一个测试文件a.txt•测试文件可以仅包含字母和数字,但不能仅有数字•测试文件中不能是单一的字符、空文件等(即不可对测试文件进行投机)–编写程序对测试文件进行压缩,并将压缩结果保存为一个新文件–编写程序对压缩后的新文件进行解压–要求“无损压缩”,即(源文件Æ压缩Æ解压)==(源文件)–要求用一个程序实现压缩和解压,以main 函数参数的形式提供压缩和解压的选项2011-3-21具体实现•基本要求的具体实现要求–扫描文件产生每个字符的使用频率–对每个字符根据使用频率建立最小堆–由最小堆建立Huffman 树–自行建立一个测试文件a.txt–用Huffman 树对测试文件进行压缩,并将压缩文件保存为a.hfm –将Huffman 树存储在a.hfm 的头部–以main 函数参数的形式提供解压的选项,以a.hfm 恢复出a.txt2011-3-21扩展要求•扩展要求–实现Holy Bible, Authorized (King James) Version (圣经,詹姆斯.金版见附件:kjv.rar)的压缩和解压缩–计算由Huffman 树给的压缩算法的压缩比•(压缩比=压缩后的文件大小/压缩前的文件大小)–尝试以下3种压缩方式• a. 扫描整个文件,以每个字符实际出线的频率计算Huffman 数• b. 不扫描整个文件,以英文字符的平均出线频率计算huffman 树• c. 扫描整个文件,以英文单词做为编码对象–提示•由于原文中是区分大小写的,并且要处理标点符号、数字等因此需要对0~255的ASCII 字符重新进行Huffman 编码•不同的Huffman 编码策略可以由main 函数的参数给出(参考字符界面的RAR)2011-3-21完整的作业要求•格式要求:•1.要求提交实验报告和源代码。
Huffman编码在数据压缩中的实际应用案例
Huffman编码在数据压缩中的实际应用案例Huffman编码是一种常用的数据压缩算法,它通过利用字符出现频率的统计信息,将出现频率较高的字符用较短的编码表示,从而实现数据的高效压缩。
本文将介绍Huffman编码在实际应用中的各种案例。
1. 文本文件压缩文本文件是最常见的需要进行压缩的数据类型之一。
Huffman编码可以通过分析文本中出现的字符及其频率,为每个字符生成唯一的编码。
根据字符出现的频率不同,生成的编码长度也不同,使得出现频率较高的字符可以用更短的编码表示,从而实现对文本文件的有效压缩。
例如,一个包含大量英文字符的文本文件,使用Huffman编码可以将每个字符表示为一个较短的二进制序列,从而极大地减少文件大小。
2. 图像压缩图像是另一个常见的需要进行压缩的数据类型。
Huffman编码在图像压缩中的应用主要体现在色彩编码上。
对于彩色图像,Huffman编码可以将不同的颜色值映射为不同的二进制序列,使得出现频率较高的颜色可以用较短的编码表示。
通过使用Huffman编码,可以将图像文件压缩为更小的文件大小,而且在解压缩时能够恢复高质量的图像。
3. 音频压缩音频文件的压缩通常是有损压缩,即通过减少音频数据的冗余和不重要的部分来实现压缩。
Huffman编码可以用于对音频信号进行压缩编码,特别是在语音文件压缩中应用广泛。
通过对语音中的音频信号进行采样和量化,并使用Huffman编码对采样后的数据进行压缩,可以显著减少语音文件的大小。
这在电信领域中被广泛应用于语音通信和存储。
4. 视频压缩类似于音频压缩,视频压缩也是有损压缩的一种形式。
在视频压缩中,Huffman编码常常与其他压缩算法(如离散余弦变换或小波变换)结合使用,以进一步减小文件大小。
Huffman编码可以用于对视频中的图像帧进行编码,从而减少文件大小并提高传输和储存效率。
例如,在MPEG标准中,Huffman编码被用于对DCT变换后的视频图像进行熵编码,以实现高效的视频压缩。
huffman编码译码实现文件的压缩与解压.
数据结构课程设计题目名称:huffman编码与解码实现文件的压缩与解压专业年级:组长:小组成员:指导教师:二〇一二年十二月二十六日目录一、目标任务与问题分析 (2)1.1目标任务 (2)1.2问题分析 (2)二、算法分析 (2)2.1构造huffman树 (2)2.1.1 字符的统计 (2)2.1.2 huffman树节点的设计 (2)2.2构造huffman编码 (3)2.2.1 huffman编码的设计 (3)2.3 压缩文件与解压文件的实现 (3)三、执行效果 (4)3.1界面 (4)3.2每个字符的编码 (4)3.3操作部分 (5)3.4文件效果 (6)四、源程序 (7)五、参考文献 (16)huffman编码与解码实现文件的压缩与解压一、目标任务与问题分析1.1目标任务采用huffman编码思想实现文件的压缩和解压功能,可以将任意文件压缩,压缩后也可以解压出来。
这样即节约了存储空间,也不会破坏文件的完整性。
1.2问题分析本问题首先应该是利用哈夫曼思想,对需要压缩的文件中的个字符进行频率统计,为了能对任意的文件进行处理,应该所有的文件以二进制的方式进行处理,即对文件(不管包含的是字母还是汉字)采取一个个的字节处理,然后根据统计的频率结果构造哈夫曼树,然后对每个字符进行哈夫曼编码,然后逐一对被压缩的文件的每个字符构建的新的哈夫曼编码存入新的文件中即得到的压缩文件。
解压过程则利用相应的哈夫曼树及压缩文件中的二进制码将编码序列译码,对文件进行解压,得到解压文件。
二、算法分析2.1构造huffman树要利用哈夫曼编码对文本文件进行压缩,首先必须知道期字符相应的哈夫曼编码。
为了得到文件中字符的频率,一般的做法是扫描整个文本进行统计,编写程序统计文件中各个字符出现的频率。
由于一个字符的范围在[0-255]之间,即共256个状态,所以可以直接用256个哈夫曼树节点即数组(后面有节点的定义)空间来存储整个文件的信息,节点中包括对应字符信息,其中包括频率。
哈夫曼编码的压缩与解压缩
哈夫曼编码的压缩与解压缩1.引言1.1 概述哈夫曼编码是一种常用的数据压缩算法,它采用了一种变长编码方式,将出现频率高的字符用较短的编码表示,出现频率低的字符用较长的编码表示,以达到压缩数据的目的。
该编码方法由美国数学家大卫·哈夫曼于1952年提出,被广泛应用于各种数据压缩和传输领域。
在传统的固定长度编码中,每个字符都使用相同的位数来表示,因此在表示不同概率出现的字符时,可能会浪费大量的位数。
而哈夫曼编码则是根据字符在文本中出现的频率来确定其对应的编码,使得高频出现的字符用更短的编码表示,低频出现的字符用较长的编码表示。
哈夫曼编码的核心思想是构建一棵哈夫曼树,将出现频率作为权值,频率越高的字符离根节点越近。
通过从叶子节点到根节点的路径确定每个字符的编码,即将左子树标记为0,右子树标记为1。
在对文本进行压缩时,将文本中的字符转换为其对应的哈夫曼编码,即可将原始数据压缩为较短的二进制串。
相比于传统固定长度编码,哈夫曼编码具有显著的优势。
首先,它可以根据文本中字符出现的实际情况进行编码,使得频率高的字符用较短的编码表示,从而大幅度减少了编码后的数据长度。
其次,哈夫曼编码是一种前缀编码,即任何一个字符的编码都不是其他字符编码的前缀,这样在解码时可以直接根据编码找到对应的字符,无需回溯查表,提高了解码效率。
哈夫曼编码的应用广泛,它被用于无损图像压缩、音频压缩、文本压缩等领域。
随着互联网和大数据时代的到来,数据的传输和存储成为重要的问题,如何高效地压缩和解压缩数据成为一个热门的研究方向。
哈夫曼编码作为一种简单而有效的压缩算法,具有广阔的应用前景。
然而,哈夫曼编码也存在一些问题,如编码时需要构建哈夫曼树,需要额外的空间和时间开销,对于小规模数据可能会影响压缩效率。
因此,在实际应用中,需要综合考虑数据规模和应用场景,选择合适的压缩算法。
1.2 文章结构本文主要介绍了哈夫曼编码的压缩与解压缩。
文章分为引言、正文和结论三个部分。
常见的文本压缩算法
常见的文本压缩算法1. Huffman 编码:Huffman 编码是一种基于概率分布的编码方法,通过统计文本中字符出现的频率,然后构建一个 Huffman 树,按照编码规则对每个字符进行编码。
频率较高的字符使用较短的编码,频率较低的字符使用较长的编码,这样可以实现对文本的压缩。
2. Lempel-Ziv-Welch (LZW) 编码:LZW 编码是一种基于字典的编码方法。
它使用滑动窗口技术来寻找文本中的重复子串,并将其替换为短的编码。
在编码过程中,不断更新和扩展字典,以便能够更好地压缩文本。
3. Burrows-Wheeler Transform (BWT):BWT 是一种重排文本字符的方法。
它通过将字符重新排序,使得文本中相邻的字符具有更高的相似性,从而方便后续的压缩工作。
BWT 常常与 Move-To-Front (MTF) 编码结合使用,MTF 编码根据字符上一次出现的位置来对字符进行编码。
4. Arithmetic Coding:算术编码是一种根据字符的概率分布来进行编码的方法。
它将整个文本视为一个连续的区间,根据字符的概率来更新区间的范围,并将最终的区间编码为一个二进制数。
算术编码可以实现非常高效的压缩,但也需要较大的计算量。
5. Run-Length Encoding (RLE):RLE 是一种简单的压缩算法,它将连续重复的字符序列替换为一个字符和重复次数的组合。
例如,字符串"AAAAABBBBCCCC" 可以被压缩为 "A5B4C4"。
6. Huffman-RLE:Huffman-RLE 是 Huffman 编码和 RLE 的结合。
它首先使用 RLE 对文本进行预处理,将连续重复的字符序列替换为一个字符和重复次数的组合,然后再使用 Huffman 编码对处理后的文本进行压缩。
这些算法都有各自的优缺点,适用于不同的压缩场景。
在实际应用中,常常会将它们结合起来使用,以提高压缩效率。
Huffman的应用之文件压缩与解压缩汇总
1).因为有些特殊的字符编码,所以我们统计字符出现的次数的时候应该用的是unsigned char,刚开始我用的文件结束标志是EOF在ASII中它的编码是-1此时已经不可以用EOF来判断是否文件结束了,所以我用了feof这个函数来判断文件是否结束.
template<class T>
struct HuffmanTreeNode
{
T _weight;
HuffmanTreeNode<T> *_left;
HuffmanTreeNode<T> *_right;
HuffmanTreeNode<T> *_parent;
HuffmanTreeNode(const T& w=T())
Huffman的应用之文件压缩与解压缩
最近这段时间一直在学习树的这种数据结构,也接触到了Huffman树以及了解了什仫是Huffman编码,而我们常用的zip压缩也是利用的Huffman编码的特性,那仫是不是可以自己实现一个文件压缩呢?当然可以了.在文件压缩中我实现了Huffman树和建堆Heap的代码,zip压缩的介绍>
{}
HuffmanTree(const T* a,size_t size)
:_root(NULL)
{
_root=_CreatHuffmanTree(a,size);
}
//将未出现的字符过滤出来,不构造堆
HuffmanTree(const T* a,size_t size,const T& invalid)
下面是我举的一个简单的范例,模拟压缩和解压缩的过程,希望有读者有帮助
使用huffman编码压缩与解压缩(python)
使⽤huffman编码压缩与解压缩(python)⼀、huffman 编码1.1 huffman 编码介绍哈夫曼编码(Huffman Coding),⼜称霍夫曼编码,是⼀种编码⽅式,哈夫曼编码是可变字长编码(VLC)的⼀种。
Huffman于1952年提出⼀种编码⽅法,该⽅法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,⼀般就叫做Huffman编码(有时也称为霍夫曼编码)huffman 编码是最优码,也是即时码(证明较为复杂,在此不给出证明)1.2 huffman 编码此处介绍⼆元 huffman 编码给定⼀个字符集合 S={s0,s1,⋯,s q},每个字符的概率为 P={p0,p1,⋯,p q}将字符集合按概率由⼤到⼩重新排列,即使得p i≥p i+1将最末尾的两个字符s q−1和s q合并,记为s′,s′的概率为p′=p q−1+p q如果剩余的字符数不为 1,则回到第 1 步huffman 编码的过程⼤致就上述三步当编码完毕后,会构建出⼀棵码树,每个码字的编码可以从码树中获得举个简单的例⼦,对于字符集合 S={A,B,C},概率为 P=0.5,0.3,0.2将B,C合并,记为BC,p(BC)=p(B)+p(C)=0.3+0.2=0.5然后将A,BC合并,记为ABC,p(ABC)=p(A)+p(BC)=0.5+0.5=1记根节点 ABC 码字为空,从根节点向下遍历,往左⾛码字末尾添0,往右⾛添1,那么 A, B, C 的码字分别为 0, 10, 11,编码表就是 {A:0,B:10,C:11}1.3 huffman 编程实现见后⽂的代码实现1.4 测试⾸先在代码⽬录下新建⼀个newfile.txt,⾥边写⼊ABCDEFG下⾯的代码实现了读取newfile.txt并对其压缩,将结果输出⾄output.enc然后对output.enc进⾏解压缩,将解压缩结果输出⾄output.dec# 读⼊⽂件with open('newfile.txt', 'rb') as fp_in:str_bytes = fp_in.read()# 构建huffman编码fre_dic = bytes_fre(str_bytes)huffman_dic = build(fre_dic)# 对⽂本进⾏编码str_enc, padding = encode(str_bytes, huffman_dic, False)# 输出⾄⽂件 output.encwith open('output.enc', 'wb') as fp_out:fp_out.write(str_enc)# 对编码后的⽂本进⾏解码str_dec = decode(str_enc, huffman_dic, padding, False)# 输出⾄⽂件 output.decwith open('output.dec', 'wb') as fp_out:fp_out.write(str_dec)# 打印huffman字典和填充位print('huffman_dic:', huffman_dic)print('padding:', padding)观察压缩前和压缩后的⽂件,发现经过我们的程序压缩之后,⽂件⼩了很多使⽤ winhex 打开 newfile.txt, output.enc, output.dec 查看对应的⼗六进制数值观察 newfile.txt 和 output.dec 的⼗六进制数值,发现⼀模⼀样,说明我们的压缩和解压并没有问题,程序正确接下来来分析 output.enc ⽂件的内容output.enc 中的数值为 C1 4E 50,转化成⼆进制为 11000001 01001110 01010000Loading [MathJax]/jax/output/HTML-CSS/jax.js我们将中间构造出的编码表打印出来,可以得到编码表字符A B C D E F G编码11000001010011100101以及填充位 padding 的值为 4我们对 newfile.txt 中的字符 ABCDEFG 进⾏编码,结果为 11 000 001 010 011 100 101。
哈夫曼编码使用哈夫曼编码进行数据压缩与解压缩
哈夫曼编码使用哈夫曼编码进行数据压缩与解压缩在信息技术发展的背景下,数据的传输和存储需求越来越大,如何高效地进行数据压缩成为了重要的课题之一。
哈夫曼编码是一种基于信息熵的编码技术,通过构建最优二叉树,实现了数据的高效压缩和解压缩。
在本文中,我们将探讨哈夫曼编码的原理和应用。
一、哈夫曼编码的原理哈夫曼编码是一种变长编码,它根据字符出现的频率构建一棵最优二叉树,频率较高的字符被赋予较短的编码,频率较低的字符则被赋予较长的编码。
这种编码方式具有无重复前缀码性质,即任意字符的编码都不是其他字符编码的前缀。
这样一来,在解码时就能够准确还原原始数据。
具体实现过程如下:1. 统计字符频率:对待编码的数据进行字符频率统计,得到每个字符出现的次数。
2. 构建哈夫曼树:根据字符频率构建一棵哈夫曼树。
树的每个节点表示一个字符,并且字符的频率决定节点的权重。
构建过程中,每次选择权重最小的两个节点合并形成新的节点,直到所有节点合并为一棵树。
3. 分配编码:从根节点出发,对哈夫曼树的每个子树进行遍历,左子树路径上追加0,右子树路径上追加1,直到到达叶节点。
这样,每个字符都能够找到对应的编码。
4. 进行数据压缩:根据字符的编码,将原始数据进行编码替换,形成压缩后的数据。
编码后的数据长度会变短,达到了数据压缩的目的。
二、哈夫曼编码的应用哈夫曼编码在数据压缩领域有着广泛的应用。
其中,最常见的应用场景是在无损压缩算法中。
通过哈夫曼编码,能够将原始数据进行高效压缩,并在解压缩时准确还原数据,但并不损失任何信息。
此外,哈夫曼编码还经常用于文件压缩、图像压缩和音频压缩等领域。
在文件压缩中,能够将大文件转换为更小的压缩文件,方便传输和存储。
在图像压缩中,通过对图像数据进行编码,能够减小图像文件的大小,提高传输效率。
在音频压缩中,通过压缩音频数据,减小文件大小,同时保持音频质量,实现高质量的音频传输。
三、哈夫曼编码的优缺点1. 优点:哈夫曼编码是一种高效的数据压缩技术,可以大幅度减小数据的存储和传输空间。
基于哈夫曼编码实现文本文件的压缩和解压缩实验报告模板
本科学生设计性实验报告软件工程技能实践Ⅰ项目组长陈启学号_*******专业软件工程班级_15软件7 班成员陈启杨林昌邓志远万胜实验项目名称_指导教师及职称__讲师__开课学期2015 至2016 学年第二学期一、实验设计方案1、实验任务与目的(简单介绍实验内容,说明实验任务和目的)1.1实验内容根据ascii码文件中各ascii字符出现的频率情况创建Haffman树,再将各字符对应的哈夫曼编码写入文件中,实现文件压缩。
对于给定的一组字符,可以根据其权值进行哈夫曼编码,并能输出对应的哈夫曼树和哈夫曼编码;实现哈夫曼解码。
能够分析文件,统计文件中出现的字符,再对文件进行编码,实现文件的压缩和解压缩,能够对于文件的压缩,比例进行统计,能够打印文件。
分析与设计哈夫曼树的存储结构,实现哈夫曼算法以及编码与译码基本功能,并对任意文本文件利用哈夫曼编码进行压缩得到压缩文件,然后进行解压缩得到解压文件。
1.2实验任务和目的1.2.1了解文件的概念。
1.2.2掌握线性链表的插入、删除等算法。
1.3.3掌握Huffman树的概念及构造方法。
1.4.4掌握二叉树的存储结构及遍历算法。
1.5.5利用Huffman树及Huffman编码,掌握实现文件压缩的一般原理。
2、实验思路(详细描述解决问题的整体思路、涉及的算法思想及数据结构等)2.1整体思路2.2涉及的算法思想及数据结构:2.2.1输入要压缩的文件首先运行的时候,用户主界面上有菜单提示该如何使用软件,根据菜单提示选择所要执行的项,依次进行,因为各个环节之间有先后顺序。
第一步为输入压缩软件的名称,由键盘输入文件路径和文件名称,读入字符数组中,打开该文件,按照提示进行压缩。
若打不开,则继续输入。
2.2.2读文件并计算字符频率文件将信息存放在字符数组中;计算每个字符出现的次数,申请一个结构体数组空间,用读取的字符减去字符结束符作为下标记录字符的频率。
2.2.3根据字符的频率,利用Huffman编码思想创建Huffman树将所记录的字符的频率作为权值来创建Huffman树,依次选择权值最小的两个字符作为左右孩子,其和作为父结点的权值,依次进行下去,直到所有的字符结点都成为叶子结点。
实验2 Huffman编码对英文文本的压缩和解压缩
《信息论与编码》实验报告班级:学号:姓名:完成时间:2011年6月2日实验2 Huffman编码对英文文本的压缩和解压缩一、实验内容根据信源压缩编码——Huffman编码的原理,制作对英文文本进行压缩和解压缩的软件。
要求软件有简单的用户界面,软件能够对运行的状态生成报告,分别是:字符频率统计报告、编码报告、压缩程度信息报告、码表存储空间报告。
二、实验环境1.计算机2.Windows 2000 或以上3.Microsoft Office 2000 或以上4.VC++ 6.05.MSDN6.0三、实验目的1.掌握Huffman编码的原理2.掌握VC开发环境的使用(尤其是程序调试技巧)3.掌握C语言编程(尤其是位运算和文件的操作)4.掌握数据结构的内容:链表、顺序表、堆栈、最优二叉树5.掌握结构化程序分析和开发的软件工程原理四、实验要求1.提前预习实验,认真阅读实验原理。
2.认真高效的完成实验,实验过程中服从实验室管理人员以及实验指导老师的管理。
3.认真填写实验报告。
要求有实验问题、实验原理、Matlab的源程序以及实验结果(实验内容中)。
4.每个同学必须独立完成实验(不能抄袭,否则两人均为零分),实验成绩是该门课程成绩的主要依据。
五、实验原理1.压缩/解压缩流程压缩流程:解压缩流程:2.Huffman编码算法(略)3.文件操作和位运算(略)六、Huffman 算法的8种不同实现方式1. huffman_a 使用链表结构生成Huffman树的算法,这是最基本的实现方法,效率最低。
2. huffman_b 使用《数据结构》(严蔚敏,吴伟民,1997,C语言版)中给出的算法,将二叉树存放在连续空间里(静态链表),空间的每个结点内仍有左子树、右子树、双亲等指针。
3. huffman_c 使用Canonical Huffman编码,同时对huffman_b的存储结构进行改造,将二叉树存放在连续空间tree里,空间的每个结点类型都和结点权值的数据类型相同,空间大小为2*num,tree[0]未用,tree[1..num]是每个元素的权值,生成Huffman后,tree[1..2*num-1]中是双亲结点索引。
基于哈夫曼编码实现文本文件的压缩和解压缩
#include <iostream>#include <sys/stat.h>#ifndef _UNISTD_H#define _UNISTD_H#include <io.h>#include <process.h>#endif /* _UNISTD_H */#include <stdio.h>#include<stdlib.h>#include <windows.h>#include <fstream>#include <vector>#include <algorithm>#include <cstring>using namespace std;typedef struct{int weight;int parent,lchild,rchild;}HTNode,*HuffmanTree;typedef struct{char* data;int* num;int length;}TNode;typedef struct{char *data;char** HM;}Code;typedef char** HuffmanCode;bool IfEncode(){int choose;system("color 70");cout<<"\t 1.加密压缩 2.无密压缩"<<endl;cout<<"请输入选择:";cin>>choose;if(choose==1)return true;elsereturn false;}void Encode(vector<char>& v){char ch[30];v.push_back('@');system("color 70");cout<<"请输入压缩密码"<<endl;cin>>ch;for(int i=0;ch[i]!='\0';i++)v.push_back(ch[i]);v.push_back('\0');v.push_back('@');cout<<"开始压缩!"<<endl;}void ReadTxt(vector<char>& v){char ch;ifstream infile("test.txt",ios::in);if(!infile){cerr<<"open error"<<endl;exit(1);}if(IfEncode())Encode(v);while(infile.peek()!=EOF){infile.get(ch);v.push_back(ch);}infile.close();}void InitList(TNode& T){T.data=new char[256];T.num=new int[256];if(!T.data||!T.num)exit(1);T.length=0;}bool Find(TNode T,char ch){int i;for(i=0;i<T.length;i++)if(ch==T.data[i])return true;return false;}void TCount(vector<char> v1,TNode &T){int i,j=0;char ch;int m=v1.size();for(i=0;i<m;i++){ch=v1[i];if(!Find(T,ch)){T.data[j]=ch;T.num[j]=count(v1.begin(),v1.end(),ch);j++;T.length++;}}}void Select(HuffmanTree &HT,int m,int& s1,int& s2) {int k,j,n,min=32767;for(k=1;k<=m;k++){if(HT[k].parent==0)if(HT[k].weight<=min){j=k;min=HT[k].weight;}}s1=j;HT[j].parent=1;min=32767;for(k=1;k<=m;k++){if(HT[k].parent==0)if(HT[k].weight<=min){n=k;min=HT[k].weight;}}s2=n;}void CreateHuffmanTree (HuffmanTree &HT,TNode T,int length) {int m,i,s1,s2;if(length<=1)return;m=2*length-1;HT=new HTNode[m+1];for(i=1;i<=m;++i){HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}for(i=1;i<=length;++i)HT[i].weight=T.num[i-1];for(i=length+1;i<=m;i++){Select(HT,i-1,s1,s2);HT[s1].parent=i;HT[s2].parent=i;HT[i].lchild=s1;HT[i].rchild=s2;HT[i].weight=HT[s1].weight+HT[s2].weight;}}void CreatHuffmanCode (HuffmanTree HT,HuffmanCode &HC,int n) {int i,f,c,start;HC=new char*[n+1];char* cd=new char[n];cd[n-1]='\0';for(i=1;i<=n;i++){start=n-1;c=i;f=HT[i].parent;while(f!=0){--start;if(HT[f].lchild==c)cd[start]='0';elsecd[start]='1';c=f;f=HT[f].parent;}HC[i]=new char[n-start];strcpy(HC[i],&cd[start]);}delete cd;}int getFileSizeSystemCall(char * strFileName){struct stat temp;stat(strFileName, &temp);return temp.st_size;}void Zip(HuffmanCode HC,vector<char> v,TNode T){int i=0,j=0,k=0;ofstream outfile("zip.txt",ios::out);if(!outfile){cerr<<"open error"<<endl;exit(1);}cout<<"输出哈夫曼编码"<<endl;for(i=0;i<v.size();i++){for(j=0;j<T.length;j++)if(T.data[j]==v[i])break;for(k=0;HC[j+1][k]!='\0';k++){ cout<<HC[j+1][k]<<" ";}for(k=0;HC[j+1][k]!='\0';k++)outfile<<HC[j+1][k];}cout<<endl;outfile.close();cout<<"正在压缩。
霍夫曼对英文文档编码解码
霍夫曼树编码一、问题描述对带压缩文本进行扫描,统计文中各符号出现的次数,利用二叉堆构造霍夫曼编码树,并将霍夫曼编码进行压缩、发送、解压。
二、基本要求1.压缩过程(1)扫描待压缩文本并统计文中各符号出现的频率/次数(使用一大小为256的整形数组)。
(2)找出有效符号的个数,比如n。
动态创建一大小为2n-1的结构数组,用于构建霍夫曼编码树。
(3)霍夫曼编码树的创建需要在数组中不断地找出频率属性最小者和次小者。
使用二叉堆来实现。
(4)遍历霍夫曼编码树,计算出各符号的码字。
(5)再次扫描文本,使用霍夫曼编码生成新的压缩文件。
文件内容构成:压缩内容字节数+最后一字节有效位数+符号频率数组(256)+压缩内容。
2.解压缩过程(1)打开已压缩文件,读出符号频率数组内容,构建解码用霍夫曼编码树。
(2)从文件中顺序读出二进制码位,利用编码树进行解码。
一个符号的解码过程也是一次从根节点到叶节点的遍历过程。
三、测试数据扫描word.txt文本,内容如下:i am so clever!guad asdfgiugfyiuwqb asffgyicbiafgiyqyrfg acbiyqwgf cbqu,cbwuqifgrq.cwuqvgqgbcaeecbyiaydgcyaib auifgyiuagf四、算法思想本次实习是在Huffman编码及其解码基础上,对编码内容进行压缩,接收端进行解压解码。
所以Huffman建树、void Compress(int codewords[],int code_len,int weight[],int t)压缩函数以及void Decompress(int w[],int wt[])解压函数是实现问题要求的主要函数。
1.huffman算法(1)以权值分别W1,W2,...Wn的n个结点,构成n棵二叉树F={T1,T2...Tn},其中每棵二叉树只有一个权值为Wi的根结点。
(2)在F中选取两棵根结点权值最小的树作为左右子树构造一棵新二叉树,且新二叉树根结点权值为左右子树权值之和。
Huffman压缩和解压
Huffman压缩和解压一、需求分析1. 本演示程序中,模拟的是小写26个英文字母的Huffman压缩,字母小随机函数随机产生,后统计字母个数建立Huffman树,用建立Huffman树将字母转为二进制流,再将二进制流每次分8个转为一个Unsigned Char写入物理内存!2. 解压时建立同样的Huffman树或读取已保存的Huffman树,通过将读取的Unsigned Char 转为比特流,再由比特流得到原被压缩的字母3. 程序执行的命令有:随机文件的产生,文件统计,Huffman树的建立,压缩,解压,程序的运行过程和其它信息的文件存储!4. 程序测试:运行程序,产生随机文件input.txt,压缩时得到的比特流bytes.txt,压缩文件compress.txt,解压时到得的比特流uncompress.txt,解压后文件output.txt和程序的运行过程文件Run.txt,其中红色文件为可无文件二、概要设计1. 为达到压缩效果,采用Huffman树来进行编码压缩2. Huffman编码后若直接保存比特流至txt将达不到压缩的效果,因此建议将比特流转为Unsigned char 来间接存储以达到明显的压缩效果3. 解压时因用到之前的Huffman树,因此在压缩时有必要保存树的状态4. Huffman树在压缩时直接使用对应编码即可,在解压时通过遍历树来得到被编码的字母三、详细设计1. 树结构体的定义和全局变量的定义:typedef struct HTNode{/*Huffman Tree 的结构定义*/unsigned int weight;unsigned int parent, lchild, rchild;}HTNode,* HuffmanTree;typedef char** HuffmanCode;/*指向字符指针的指针的数据的定义*/int stat[27];/*存储总数,存储26个字母出现的次数*/HuffmanTree HT;HuffmanCode HC;2. 随机文件的产生函数:void CreatFile(){/*自动产生文件*/FILE *Fopen,*Fout;int i;srand( (unsigned)time(NULL));Fout=fopen("./Run.txt","w");fprintf(Fout,"NO.1: Creating a file by rand()"n");Fopen = fopen("./input.txt", "w");for(i=0; i<10000; i++){char ch=(char)(97+rand()%26);fputc(ch, Fopen);}fclose(Fopen);fclose(Fout);}3. 字母个数的统计:void ReadStat(char *Filename){/*每个字符出现的次数统计*/ int c;FILE *Fopen=fopen(Filename, "r");while((c=fgetc(Fopen))!=EOF){stat[c%96]++;stat[0]++;}}4. 选择HT[1…i-1]中Parent为0且Weight最小的一个点的函数:void Select(HuffmanTree HT, int d, int *s){int temp=0,i;for(i=1; i<=d; i++){if(HT[i].parent!=0)continue;if(temp==0)temp=i;else if(HT[temp].weight>HT[i].weight)temp=i;}*s=temp;}5. Huffman树的建立函数:void HuffmanCoding(int* w, int n){int m= 2*n-1;int i, s1, s2, start;unsigned int c, f;char *cd;if(n<=1)return;HT=(HTNode*)malloc((m+1)*sizeof(HTNode));/*0号单元未用*/for(i=1; i<=n; i++, w++){/***p={*w,0,0,0}; 初始化*/HT[i].weight=*w;HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}for(; i<=m; i++){/**p={0,0,0,0};*/HT[i].weight=*w;HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;}for(i=n+1; i<=m; i++){Select(HT, i-1, &s1);/*选择HT[1…i-1]中Parent为0且Weight最小的一个点*/ HT[s1].parent=i;Select(HT, i-1, &s2);HT[s2].parent=i;HT[i].lchild=s1;/*左右孩子对编码没有影响,也说明的Huffman树的不唯一性*/HT[i].rchild=s2;HT[i].weight= HT[s1].weight + HT[s2].weight;}HC = (HuffmanCode) malloc ((n+1) * sizeof(char*));/*分配n个字符编码的头指针向量*/cd = (char*) malloc(n*sizeof(char));cd[n-1]='"0';for(i=1; i<=n; i++){start =n-1;for(c=i, f=HT[i].parent; f!=0; c=f, f=HT[f].parent)if(HT[f].lchild==c)cd[--start]='0';elsecd[--start]='1';HC[i]=(char*)malloc((n-start)*sizeof(char));strcpy(HC[i], &cd[start]);}free(cd);}6. Huffman树的存储:void WriteCode(){FILE *Fopen;int i;Fopen=fopen("./Run.txt","a+");fprintf(Fopen,"NO.2: The array of the structure of HuffmanTree:"n");for(i=1;i<52;i++)fprintf(Fopen,"%-2d: p:%-3d w:%-5d l:%-3d r:%-3d"n",i,HT[i].parent,HT[i].weight,HT[i].lchild,HT[i].rchild);fprintf(Fopen,"NO.3: The Huffman codes:"n");for(i=1;i<27;i++)fprintf(Fopen,"%c: %3d %s"n",'a'+i-1,stat[i],HC[i]);}7. 输出压缩前应该得到的比特流:void OutCompress(){FILE *Fin,*Fout;int c;Fin=fopen("./input.txt","r");Fout=fopen("./bytes.txt","w");if(Fin==NULL)printf("Can't find the file of 'input'!"n");while((c=fgetc(Fin))!=EOF){/*output the bit stream*/fprintf(Fout,"%s",HC[c-96]);}fclose(Fin);fclose(Fout);}8. 压缩函数:void Compress(){FILE *Fin,*Fout;unsigned char out=0;char c,*buf;int i,count=0;buf=(char *)malloc(sizeof(char)*20);Fin=fopen("./input.txt","r");Fout=fopen("./compress.txt","wb");if(Fin==NULL)printf("Can't find the file of 'input'!"n");while((c=fgetc(Fin))!=EOF){/*output the bit stream*/ buf=HC[c-96];for(i=0;buf[i]!='"0';i++){if(count==8){fprintf(Fout,"%c",out);count=0;out=0;}out<<=1;count++;if(buf[i]=='1')out=out|1;}}if(count!=8){out<<=(8-count);fprintf(Fout,"%c",out);}fclose(Fin);fclose(Fout);}9. 解压函数:void UnCompress(){FILE *Fin,*Fout,*Fout1;unsigned char c=0,*buf;int i,k,t=51;buf=(char *)malloc(sizeof(char)*10);Fin=fopen("./compress.txt","rb");Fout=fopen("./uncompress.txt","wb");Fout1=fopen("./output.txt","wb");if(Fin==NULL)printf("Can't find the file of 'input'!"n");while(!feof(Fin)){/*output the bit stream*/c=fgetc(Fin);for(i=7;i>=0;i--){k=(c>>i)&1;fprintf(Fout,"%d",k);if(k==0)t=HT[t].lchild;elset=HT[t].rchild;if(HT[t].lchild==0&&HT[t].rchild==0){fprintf(Fout1,"%c",96+t);t=51;}}}fclose(Fin);fclose(Fout);}10. 主函数入口int main(int arg, char *argv[]){/*存放Huffman编码*/CreatFile();ReadStat("./input.txt");HuffmanCoding(&stat[1], 26);WriteCode();OutCompress();Compress();UnCompress();system("Echo Pree any key to kill the program");system("PAUSE>NUL 2>NUL");}四、调试分析1. 二进制流与unsigned char之间的转换,采用的是位操作,而不是进制的转换,导致失败2. 位操作的使用出错,将c<<1错误理解为对C的左移,导致无法成功压缩,后改为C=<<1即可3. 文件的读入方式有二进制读入和文本读入,当压缩文件中写入^Z(文本文件结束标志)后,文本方式读取压缩文件失败,最后使用二进制读取成功4. 文件的压缩效率问题上我考虑过用unsigned int来替换unsigned char,但结果是失败的,压缩率反而降低5. 由于比特流中二进制数%sizeof(unsigned char)不一定为零,此异常(解压文件结尾处会多出2个字符)处理仍没有得到有效解决!6. 因为输入文件是随机为生,每个字母出现的频率相近,因此得到的Huffman编码长度也基本相同,在这种情况也就是最差的情况下,最后压缩率达到60%,让人振奋!五、测试结果:经多次按要求合法测试,测试程序执行的无误,压缩率也在60%以下,下面是测试结果的一个范例:其中输入文件大小为10,000个随机字母,大小为:10,000 字节产生的压缩文件大小为:5,939 字节解压后文件大小仍为:10,000 字节压缩率接近:59.4%#include"stdafx.h"#include"huffman.h"#include"compression.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////HTNode *ht;HCode *hcd;//int *hcdlen; //对应哈夫曼编码长度//long *cdlen; //哈夫曼编码总长度int maplist[256]; //建立字符与哈夫曼编码的映射int nodenum=0; //哈夫曼树结点数int rearnum=0; //哈夫曼编码尾补码int textlen=0; //需压缩的文件长度int codelen=0; //压缩后的文件的哈夫曼编码总长度int const bufferlen=1024; //设置读取缓冲区长度compression::compression(){}compression::~compression(){}int compression::clean(){delete[] ht;delete[] hcd;return 1;}void compression::Dec2Bin(int num,int bin[8]){int i=0;for(i=0;i<8;i++){bin[i]=0;}i=0;while(num>0){bin[8-1-i]=num%2;num=num/2;i++;}}bool compression::InitFromFile(string fileadd){fstream infile(fileadd.c_str(),ios::binary|ios::in);if(!infile){MessageBox(NULL,"无法打开文件","错误",MB_OK|MB_ICONERROR);return 0;}int table[256];int i,j;int len=0,num=0;unsigned char ch;for(i=0;i<256;i++){table[i]=0;maplist[i]=-1;}int readlen=0;char buffer[bufferlen]; //设置读取缓冲区,加快读取速度do{infile.read(buffer,bufferlen);i=0;readlen=infile.gcount();while(i<readlen){ch=(unsigned char)buffer[i];table[ch]++;len++;i++;}}while(readlen==bufferlen);for(i=0;i<256;i++){if(table[i]!=0)num++;}ht=new HTNode[2*num-1];hcd=new HCode[num];for(i=0,j=0;i<256;i++){if(table[i]!=0){ht[j].data=i;ht[j].weight=table[i];maplist[i]=j; //建立字符与哈夫曼编码的映射j++;}}nodenum=num;textlen=len;// cout<<"............文件长度:"<<len<<"获取结点数"<<nodenum<<endl;infile.clear();infile.close();return 1;}void compression::HTCreat(HTNode ht[], int n){// cout<<"............构造哈夫曼树过程"<<endl;int i,k,lnode,rnode;int min1,min2;for(i=0;i<2*n-1;i++){ht[i].parent=ht[i].rchild=ht[i].lchild=-1;}for(i=n;i<2*n-1;i++){min1=min2=2147483647;lnode=rnode=-1;for(k=0;k<=i-1;k++){if(ht[k].parent==-1){if(ht[k].weight<min1){min2=min1;min1=ht[k].weight;rnode=lnode;lnode=k;}else if(ht[k].weight<min2){min2=ht[k].weight;rnode=k;}}}ht[lnode].parent=i;ht[rnode].parent=i;ht[i].weight=ht[lnode].weight+ht[rnode].weight;ht[i].lchild=lnode;ht[i].rchild=rnode;}}///////////构造哈夫曼编码/////////////void compression::HCCreat(HTNode ht[], HCode hcd[], int n) {// cout<<"............编码过程:"<<endl;int i,p,c;HCode hc;hc=new char[n];int start,tmplen;for(i=0;i<n;i++){tmplen=0;start=n-1;hc[start]='\0';c=i;p=ht[i].parent;while(p!=-1){if(ht[p].lchild==c) //是左孩子结点{hc[--start]='0';tmplen++;}else{hc[--start]='1';tmplen++;}c=p;p=ht[p].parent;}hcd[i]=new char[n-start];strcpy(hcd[i],&hc[start]);}delete[] hc;}unsigned char compression::ConvertBinary(char *tmp){char ch=0;int i;for(i=0;i<8;i++){ch=(unsigned char)pow(2.0,8-i-1)*(tmp[i]-48)+ch;}return ch;}//////////////////压缩并写入文件//////////////////bool compression::ConvertFile(HCode hcd[], string fileadd, string fileadd2){// cout<<"............压缩并写入文件过程:"<<endl;fstream infile(fileadd.c_str(),ios::in|ios::binary);fstream outfile(fileadd2.c_str(),ios::out|ios::binary);if(!infile){MessageBox(NULL,"无法打开文件","错误",MB_OK|MB_ICONERROR);//cout<<"open file fail!"<<endl;return 0;}if(!outfile){MessageBox(NULL,"无法打开文件","错误",MB_OK|MB_ICONERROR);//cout<<"creat file fail!"<<endl;return 0;}//unsignedchar ch;/////////////写入哈夫曼树//////////////ch=nodenum;outfile.write(&ch,1); ///写入结点数ch=8;outfile.write(&ch,1); ///写入补位数(预写入)codelen=0;outfile.write((char *)&codelen,4); //写入压缩后的文件的哈夫曼编码总长度(预写入) int h=0;for(h=0;h<nodenum;h++){outfile.write((char*)&ht[h].data,sizeof(char));outfile.write((char*)&ht[h].weight,sizeof(int));}///////////////////////////char tmp[8]; //设置缓冲区char outbuffer[bufferlen]; //设置写入缓冲区char *tmpcd;int i=0,j,k,last=0;char inbuffer[bufferlen];int readlen=0;//infile.seekg(i,ios::beg);h=0;do{infile.read(inbuffer,bufferlen);readlen=infile.gcount();tmpcd=hcd[maplist[(unsigned char)inbuffer[i]]];for(i=0;i<readlen;){for(j=last;j<8 && *tmpcd!='\0';j++){tmp[j]=*tmpcd;tmpcd++;}if(j==8 && *tmpcd=='\0'){last=0;i++;////////////////////infile.seekg(i,ios::beg);//WriteFile(tmp);ch=ConvertBinary(tmp);//cout<<':'<<(unsigned int)ch<<' ';outbuffer[h]=ch;h++;codelen++; //压缩文件长加度加一if(h==bufferlen){outfile.write(outbuffer,bufferlen);h=0;}if(i<readlen)tmpcd=hcd[maplist[(unsigned char)inbuffer[i]]];else{i=0;break;}}else if(j<8 && *tmpcd=='\0'){last=j;i++;if(i<readlen)tmpcd=hcd[maplist[(unsigned char)inbuffer[i]]];else{i=0;break;}/////继续循换////}else if(j==8 && *tmpcd!='\0'){last=0;//WriteFile(tmp);ch=ConvertBinary(tmp);outbuffer[h]=ch;h++;codelen++; //压缩文件长加度加一if(h==bufferlen){outfile.write(outbuffer,bufferlen);h=0;}}}}while(readlen==bufferlen);if(j==8 && readlen<bufferlen){outfile.write(outbuffer,h);}else if(j<8 && readlen<bufferlen){for(k=j;k<8;k++){tmp[k]='0';}ch=ConvertBinary(tmp);outbuffer[h]=ch;h++;outfile.write(outbuffer,h);codelen++; //压缩文件长加度加一}// cout<<endl;ch=8-j;rearnum=8-j;// cout<<"............补位吗:"<<rearnum<<endl;outfile.seekp(1,ios::beg);outfile.write(&ch,1); //写入真正的补位数// cout<<"............压缩后文件的哈夫曼编码长度:"<<codelen<<endl;outfile.seekp(2,ios::beg);outfile.write((char*)&codelen,4); //写入真正的压缩后的文件的哈夫曼编码总长度长度outfile.close();infile.close();return 1;}bool compression::DecompressionFile(string fileadd2, string fileadd3){// cout<<"............解压并输出新文件过程:"<<endl;fstream infile(fileadd2.c_str(),ios::in|ios::binary);fstream outfile(fileadd3.c_str(),ios::out|ios::binary);if(!infile){MessageBox(NULL,"无法打开文件","错误",MB_OK|MB_ICONERROR);//cout<<"open file fail!"<<endl;return 0;}if(!outfile){MessageBox(NULL,"无法打开文件","错误",MB_OK|MB_ICONERROR);//cout<<"creat file fail!"<<endl;return 0;}// cout<<endl;/////////////////读出哈夫曼树的数据/////////////int h=0;char buffer[bufferlen]; //读入文件的缓冲区char outbuffer[bufferlen]; //写入文件的缓冲区infile.read(buffer,1);nodenum=(unsigned char)*buffer;//哈夫曼树结点数if(nodenum==0)nodenum=256;// cout<<"............读出哈夫曼树结点数:"<<nodenum<<endl;infile.read(buffer,1);rearnum=(unsigned char)*buffer;// cout<<"............读出哈夫曼编码补位数:"<<rearnum<<endl;infile.read((char*)&codelen,4);// cout<<"............读出压缩后的文件的哈夫曼编码总长度:"<<codelen<<endl;// cout<<" 读出哈夫曼树数据...."<<endl;ht=new HTNode[2*nodenum-1];hcd=new HCode[nodenum];// hcdlen=new int[nodenum];for(h=0;h<nodenum;h++){infile.read(&ht[h].data,1);infile.read((char*)&ht[h].weight,4);}//////构造哈夫曼树///////HTCreat(ht,nodenum);//////构造哈夫曼编码/////HCCreat(ht,hcd,nodenum);//////////////////////////////////////////////////////////////////////解压并输出解压文件////////////////////////// cout<<"............输出新文件"<<endl;char *buffertmp=new char;int bin[8],j=0,i=0;int coderead=0; //记录以度的长度,用于判断何时达到文件最后一字节(用codelen比较) int readlen=0;int child=0;int last=2*nodenum-2; //解压时记录上次指示器的位置child=last;unsigned char outp;h=0;do{infile.read(buffer,bufferlen);readlen=infile.gcount();for(j=0;j<readlen;j++){coderead++;outp=buffer[j];Dec2Bin(outp,bin);if(coderead==codelen) //达到文件尾{for(i=0;i<=8-rearnum;i++){if(ht[child].lchild==-1 && ht[child].rchild==-1){// cout<<ht[child].data;outbuffer[h]=ht[child].data;h++;if(h==bufferlen){outfile.write(outbuffer,bufferlen);h=0;}last=2*nodenum-2;if(i==8-rearnum){if(h!=0)outfile.write(outbuffer,h);child=last;break;}else i--;}else if(i!=8){if(bin[i]==0)last=ht[child].lchild;else if(bin[i]==1)last=ht[child].rchild;}child=last;}}else//没达到文件尾{for(i=0;i<=8;i++){if(ht[child].lchild==-1 && ht[child].rchild==-1){// cout<<ht[child].data;outbuffer[h]=ht[child].data;h++;if(h==bufferlen){outfile.write(outbuffer,bufferlen);h=0;}last=2*nodenum-2;if(i==8){child=last;break;}elsei--;}else if(i!=8){if(bin[i]==0)last=ht[child].lchild;else if(bin[i]==1)last=ht[child].rchild;}child=last;}}}}while(readlen==bufferlen);// cout<<j<<endl;infile.close();outfile.close();return 1;}bool compression::Compression(string fileadd){int i;for(i=0;i<fileadd.length();i++)if(fileadd[i]=='\\')fileadd[i]='/';string fileadd2;fileadd2=fileadd+".hfm";if(!InitFromFile(fileadd)) //从文件中初始化哈夫曼树return 0;HTCreat(ht,nodenum); //构造哈夫曼树HCCreat(ht,hcd,nodenum); //构造哈夫曼编码if(!ConvertFile(hcd,fileadd,fileadd2)) //压缩并写入文件return 0;clean();return 1;}bool compression::Decompression(string fileadd2){int i;for(i=0;i<fileadd2.length();i++)if(fileadd2[i]=='\\')fileadd2[i]='/';string fileclass;string fileadd3;for(i=fileadd2.length()-5;fileadd2[i]!='.' && i>0;i--) fileclass.insert(0,fileadd2.substr(i,1));if(i!=0)fileadd3=fileadd2.substr(0,i)+".new"+'.'+fileclass;elsefileadd3=fileadd2.substr(0,fileadd2.length()-4)+".new";if(!DecompressionFile(fileadd2,fileadd3))return 0;clean();return 1;}。
霍夫曼编码对英文文本的压缩和解压缩
Huffman编码对英文文本的压缩和解压缩中国地质大学计算机学院信息安全专业信息论实验报告#include <stdio.h>#include <string.h>#include <conio.h>struct head {unsigned char b; //记录字符在数组中的位置long count; //字符出现频率(权值)long parent,lch,rch; //定义哈夫曼树指针变量char bits[256]; //定义存储哈夫曼编码的数组}header[512],tmp;void compress(){char [255],output],buf[512];unsigned char c;long n,m,i,j,f; //作计数或暂时存储数据用long min1,pt1,flength=0,length1,length2; //记录最小结点、文件长度double div; //计算压缩比用FILE *ifp,*ofp; //分别为输入、输出文件指针printf("\t请您输入需要压缩的文件(需要路径):");gets();ifp=fopen(,"rb");if(ifp==NULL){printf("\n\t文件打开失败!\n ");system("pause");return;}printf("\t请您输入压缩后的文件名(如无路径则默认为桌面文件):");gets(outputfile);ofp=fopen(outputfile,"wb");if(ofp==NULL){printf("\n\t压缩文件失败!\n ");system("pause");return;}flength=0;while(!feof(ifp)){fread(&c,1,1,ifp);header[c].count++; //字符重复出现频率+1flength++; //字符出现原文件长度+1 }flength--;length1=flength; //原文件长度用作求压缩率的分母header[c].count--;for(i=0;i<512;i++){if(header[i].count!=0)header[i].b=(unsigned char)i;/*将每个哈夫曼码值及其对应的ASCII码存放在一维数组header[i]中,且编码表中的下标和ASCII码满足顺序存放关系*/ elseheader[i].b=0;header[i].parent=-1;header[i].lch=header[i].rch=-1; //对结点进行初始化}for(i=0;i<256;i++){ //按出现权值从大到小排序for(j=i+1;j<256;j++){if(header[i].count<header[j].count){tmp=header[i];header[i]=header[j];header[j]=tmp;}}}for(i=0;i<256;i++) //找到第一个空的header结点if(header[i].count==0)break;n=i;m=2*n-1;for(i=n;i<m;i++){min1=999999999; //预设的最大权值,即结点出现的最大次数for(j=0;j<i;j++){if(header[j].parent!=-1)continue; /*parent!=-1说明该结点已存在哈夫曼树中,跳出循环重新选择新结点*/if(min1>header[j].count){pt1=j;min1=header[j].count;continue;}}header[i].count=header[pt1].count;header[pt1].parent=i;header[i].lch=pt1;min1=999999999;for(j=0;j<i;j++){if(header[j].parent!=-1)continue;if(min1>header[j].count){pt1=j;min1=header[j].count;continue;}}header[i].count+=header[pt1].count;header[i].rch=pt1;header[pt1].parent=i; //哈夫曼无重复前缀编码}for(i=0;i<n;i++){f=i;header[i].bits[0]=0; //根结点编码0while(header[f].parent!=-1){j=f;f=header[f].parent;if(header[f].lch==j){ //置左分支编码0j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);//依次存储连接"0""1"编码,此处语句为网络借鉴header[i].bits[0]='0';}else{ //置右分支编码1j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);header[i].bits[0]='1';}}}fseek(ifp,0,SEEK_SET); //从文件开始位置向前移动0字节,即定位到文件开始位置fwrite(&flength,sizeof(int),1,ofp); /*用来将数据写入文件流中,参数flength指向欲写入的数据地址,总共写入的字符数以参数size*int来决定,返回实际写入的int数目*/ fseek(ofp,8,SEEK_SET);buf[0]=0; //定义缓冲区,它的二进制表示00000000f=0;pt1=8; /*假设原文件第一个字符是"A",8位2进制为01000001,编码后为0110识别编码第一个'0',那么将其左移一位,看起来没什么变化。
使用哈夫曼树对英文文本进行压缩的简单代码例子
使用哈夫曼树对英文文本进行压缩的简单代码例子## 一、简介哈夫曼树(Huffman Tree)是一种常用的字符编码方法,可以用来对英文文本进行压缩。
它根据字符出现的频率逐渐建立一颗二叉树,使得字符出现频率高的,用较短的编码表示,反之字符出现频率低的,用较长的编码表示。
下面给出一个简单的使用哈夫曼树对英文文本进行压缩的代码实例。
## 二、代码实例```python#!/usr/bin/env python# -*- coding: utf-8 -*-# 使用哈夫曼树对英文文本进行压缩# 统计每个字符出现的频率def get_frequency(input_str):frequency_dic = {}for c in input_str:if c in frequency_dic:frequency_dic[c] += 1else:frequency_dic[c] = 1return frequency_dic# 生成哈夫曼树def build_huffman_tree(frequency_dic):while len(frequency_dic) > 1:# 每次从频率字典中取出最小的两个字符,拼接成新字符c1, count1 = sorted(frequency_dic.items(), key=lambda x: x[1])[0]c2, count2 = sorted(frequency_dic.items(), key=lambda x: x[1])[1]new_c = c1 + c2# 构建新字符的频率字典,比如字符'ab',频率是'a'和'b'的频率之和new_count = count1 + count2# 将新字符加入到频率字典中去frequency_dic[new_c] = new_count# 将取出的最小的两个字符从频率字典中删除del frequency_dic[c1]del frequency_dic[c2]# 频率字典中剩下最后一个字符就是根节点,返回该节点return list(frequency_dic.keys())[0]# 将哈夫曼树转换成编码表def build_huffman_code(huffman_tree):# 以字典的形式返回code_dic = {}# 从根节点开始,依次深度遍历哈夫曼树 def dfs(node, code):if len(node) == 1:# 叶子节点直接根据编码表添加进字典中 code_dic[node] = codeelse:# 非叶子节点,分别从左右子节点继续遍历 dfs(node[0], code + '0')dfs(node[1], code + '1')dfs(huffman_tree, '')return code_dic# 哈夫曼编码压缩def huffman_zip(input_str, code_dic): output_str = ''for c in input_str:output_str += code_dic[c]return output_str# 主函数def main():# 需要压缩的英文文本input_str = 'this is an english text'# 统计每个字符出现的频率frequency_dic = get_frequency(input_str)print('字符出现频率:', frequency_dic)# 根据字符出现的频率,生成哈夫曼树huffman_tree = build_huffman_tree(frequency_dic) print('哈夫曼树:', huffman_tree)# 将哈夫曼树转换成编码表code_dic = build_huffman_code(huffman_tree)print('编码表:', code_dic)# 哈夫曼编码压缩output_str = huffman_zip(input_str, code_dic)print('压缩后的字符串:', output_str)if __name__ == '__main__':main()```## 三、总结上述代码中,先通过统计每个字符出现的频率构建频率字典,再根据频率字典构建哈夫曼树,然后将哈夫曼树转换成编码表,最后对英文文本使用编码表进行压缩。
旅游文本翻译过程中信息单元的压缩和解压
旅游文本翻译过程中信息单元的压缩和解压随着科技的发展,旅游文本翻译成为了翻译行业的重要内容。
翻译文本的过程中,信息单元的压缩和解压是关键的一步。
本文将通过分析文本压缩和解压的原理,从而推导出旅游文本翻译中信息单元的压缩和解压。
首先,信息单元压缩是指通过从文本中删除重复内容及无意义部分,以减少文本的大小,以节省空间。
压缩的有效性体现在文本转发速度更快、体积更小、传输更加有效等方面。
压缩文本的方法有很多种,其中,在旅游文本翻译中,最常用的是哈夫曼编码。
哈夫曼编码,也叫Huffman编码,是一种常用的信息压缩编码方式。
它是根据字符出现的频率自动生成编码,这种方法不仅能够把文本压缩到最小尺寸,而且还能够加快文本检索速度,提高翻译速度和效率。
在旅游文本翻译中,翻译者可以根据词汇出现的频率,自动生成哈夫曼编码来压缩文本,以节省传输时间和空间。
信息单元的解压是恢复文本原有体积的过程。
解压文本的方法有很多,如压缩文件的解压方法,常用的有硬盘空间节省器,压缩程序,压缩文件,比特流编码等等。
在旅游文本翻译中,解压的方法也是多样的,翻译者可以使用哈夫曼编码进行解压,哈夫曼编码可以把压缩后的文件进行解压,还原其原有的空间大小,恢复其原有的样子。
此外,旅游文本翻译中还有一种新的解压方法枯燥话编码。
枯燥话编码是一种适用于旅游文本翻译的特殊编码方式,它是为了提高翻译的效率而发明的。
它使用枯燥话表述文本,以减少文本传输时间。
例如,可以使用枯燥话来翻译旅游文本的“游客购买的门票可以享受优惠折扣”,翻译为“游客可以享受购票折扣”。
以上就是旅游文本翻译中信息单元的压缩和解压的一般情况。
实际的翻译中,信息单元的压缩和解压是一个综合考虑的过程,翻译者要根据文本的具体特点,选择合适的压缩和解压方法,以节省翻译时间和费用,提高翻译效率。
文本翻译是一项精细的工作,在翻译文本的过程中,信息单元的压缩和解压是关键的一步。
本文简要介绍了旅游文本翻译中,信息单元的常用压缩方法和解压方法,指出了翻译过程中应当针对文本具体特点选择合适的压缩和解压方法,以提高翻译效率。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Huffman编码对英文文本的压缩和解压缩中国地质大学计算机学院信息安全专业信息论实验报告#include <stdio.h>#include <string.h>#include <conio.h>struct head {unsigned char b; //记录字符在数组中的位置long count; //字符出现频率(权值)long parent,lch,rch; //定义哈夫曼树指针变量char bits[256]; //定义存储哈夫曼编码的数组}header[512],tmp;void compress(){char filename[255],outputfile[255],buf[512];unsigned char c;long n,m,i,j,f; //作计数或暂时存储数据用long min1,pt1,flength=0,length1,length2; //记录最小结点、文件长度double div; //计算压缩比用FILE *ifp,*ofp; //分别为输入、输出文件指针printf("\t请您输入需要压缩的文件(需要路径):");gets(filename);ifp=fopen(filename,"rb");if(ifp==NULL){printf("\n\t文件打开失败!\n ");system("pause");return;}printf("\t请您输入压缩后的文件名(如无路径则默认为桌面文件):");gets(outputfile);ofp=fopen(outputfile,"wb");if(ofp==NULL){printf("\n\t压缩文件失败!\n ");system("pause");return;}flength=0;while(!feof(ifp)){fread(&c,1,1,ifp);header[c].count++; //字符重复出现频率+1flength++; //字符出现原文件长度+1 }flength--;length1=flength; //原文件长度用作求压缩率的分母header[c].count--;for(i=0;i<512;i++){if(header[i].count!=0)header[i].b=(unsigned char)i;/*将每个哈夫曼码值及其对应的ASCII码存放在一维数组header[i]中,且编码表中的下标和ASCII码满足顺序存放关系*/elseheader[i].b=0;header[i].parent=-1;header[i].lch=header[i].rch=-1; //对结点进行初始化}for(i=0;i<256;i++){ //按出现权值从大到小排序for(j=i+1;j<256;j++){if(header[i].count<header[j].count){tmp=header[i];header[i]=header[j];header[j]=tmp;}}}for(i=0;i<256;i++) //找到第一个空的header结点if(header[i].count==0)break;n=i;m=2*n-1;for(i=n;i<m;i++){min1=999999999; //预设的最大权值,即结点出现的最大次数for(j=0;j<i;j++){if(header[j].parent!=-1)continue; /*parent!=-1说明该结点已存在哈夫曼树中,跳出循环重新选择新结点*/ if(min1>header[j].count){pt1=j;min1=header[j].count;continue;}}header[i].count=header[pt1].count;header[pt1].parent=i;header[i].lch=pt1;min1=999999999;for(j=0;j<i;j++){if(header[j].parent!=-1)continue;if(min1>header[j].count){pt1=j;min1=header[j].count;continue;}}header[i].count+=header[pt1].count;header[i].rch=pt1;header[pt1].parent=i; //哈夫曼无重复前缀编码}for(i=0;i<n;i++){f=i;header[i].bits[0]=0; //根结点编码0while(header[f].parent!=-1){j=f;f=header[f].parent;if(header[f].lch==j){ //置左分支编码0j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);//依次存储连接"0""1"编码,此处语句为网络借鉴header[i].bits[0]='0';}else{ //置右分支编码1j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);header[i].bits[0]='1';}}}fseek(ifp,0,SEEK_SET); //从文件开始位置向前移动0字节,即定位到文件开始位置fwrite(&flength,sizeof(int),1,ofp); /*用来将数据写入文件流中,参数flength指向欲写入的数据地址,总共写入的字符数以参数size*int来决定,返回实际写入的int数目*/fseek(ofp,8,SEEK_SET);buf[0]=0; //定义缓冲区,它的二进制表示00000000f=0;pt1=8; /*假设原文件第一个字符是"A",8位2进制为01000001,编码后为0110识别编码第一个'0',那么将其左移一位,看起来没什么变化。
下一个是'1',应该|1,结果00000001同理4位都做完,应该是00000110,由于字节中的8位并没有全部用完,继续读下一个字符,根据编码表继续拼完剩下4位,如果字符的编码不足4位,还要继续读一个字符,如果字符编码超过4位,那么把剩下的位信息拼接到一个新的字节里*/while(!feof(ifp)){c=fgetc(ifp);f++;for(i=0;i<n;i++){ //找到对应的header[i]if(c==header[i].b)break;}strcat(buf,header[i].bits);j=strlen(buf);c=0;while(j>=8){for(i=0;i<8;i++){if(buf[i]=='1')c=(c<<1)|1; //添加最后一位为1elsec=c<<1; //添加最后一位为0}fwrite(&c,1,1,ofp);pt1++;strcpy(buf,buf+8);j=strlen(buf);}if(f==flength)break;}if(j>0){ //最后不满八位的buf处理strcat(buf,"00000000"); //buf后加八位0for(i=0;i<8;i++){ //逐位输入八位前的二进制符if(buf[i]=='1')c=(c<<1)|1;elsec=c<<1;}fwrite(&c,1,1,ofp);pt1++;}fseek(ofp,4,SEEK_SET); //指针回到输出文件头部后面四位fwrite(&pt1,sizeof(long),1,ofp); //pt1统计了输出文件中的字符个数,包括了最后的'/0'fseek(ofp,pt1,SEEK_SET);fwrite(&n,sizeof(long),1,ofp); //n统计了权值不为0的header[]数量for(i=0;i<n;i++){fwrite(&(header[i].b),1,1,ofp); //依次写入每个叶子结点的b、长度和内容c=strlen(header[i].bits);fwrite(&c,1,1,ofp);j=strlen(header[i].bits);if(j%8!=0){ //若存储的位数不是8的倍数,则补0for(f=j%8;f<8;f++)strcat(header[i].bits,"0");}while(header[i].bits[0]!=0){ /*字符的有效存储不超过8位,则对有效位数左移实现两字符编码的连接,可理解为前缀编码也被压缩过*/ c=0;for(j=0;j<8;j++){if(header[i].bits[j]=='1')c=(c<<1)|1;elsec=c<<1;}strcpy(header[i].bits,header[i].bits+8);fwrite(&c,1,1,ofp); //以上与上面连接字符一段可相同理解}}length2=pt1--;div=((double)length1-(double)length2)/(double)length1; //计算文件的压缩率fclose(ifp);fclose(ofp);printf("\n\t压缩文件成功!\n");printf("\t压缩率为%f%%\n\n",div*100);return;}void uncompress(){char filename[255],outputfile[255],buf[255],bx[255];unsigned char c;long i,j,m,n,f,p,l;long flength;FILE *ifp,*ofp;printf("\t请您输入需要解压缩的文件(如无路径则默认为桌面文件):");gets(filename);ifp=fopen(filename,"rb");if(ifp==NULL){printf("\n\t文件打开失败!\n ");system("pause");return;}printf("\t请您输入解压缩后的文件名:");gets(outputfile);ofp=fopen(outputfile,"wb");if(ofp==NULL){printf("\n\t解压缩文件失败!\n ");system("pause");return;}fread(&flength,sizeof(long),1,ifp); //读入文件长度flengthfread(&f,sizeof(long),1,ifp); //读入header数组的存储地址fseek(ifp,f,SEEK_SET);fread(&n,sizeof(long),1,ifp); //读入header数组中元素的个数for(i=0;i<n;i++){ //读入header数组fread(&header[i].b,1,1,ifp);fread(&c,1,1,ifp);p=(long)c;header[i].count=p;header[i].bits[0]=0;if(p%8>0)m=p/8+1;elsem=p/8;for(j=0;j<m;j++){fread(&c,1,1,ifp);f=c;itoa(f,buf,2); /*此处借鉴网络程序,包括itoa()函数itoa()函数的作用为,把int型的f化为二进制数,再变成char型存入buf*/f=strlen(buf);for(l=8;l>f;l--){ //在单字节内对相应位置补0strcat(header[i].bits,"0");}strcat(header[i].bits,buf);}header[i].bits[p]=0;}for(i=0;i<n;i++){ //按Huffman编码从小到大排序for(j=i+1;j<n;j++){if(strlen(header[i].bits)>strlen(header[j].bits)){tmp=header[i];header[i]=header[j];header[j]=tmp;}}}p=strlen(header[n-1].bits);fseek(ifp,8,SEEK_SET);m=0;bx[0]=0;while(1){ //对文件其余部分,即真正的文件部分解压缩while(strlen(bx)<(unsigned int)p){fread(&c,1,1,ifp);f=c;itoa(f,buf,2);f=strlen(buf);for(l=8;l>f;l--){strcat(bx,"0");}strcat(bx,buf);}for(i=0;i<n;i++){ //依次比对Huffman前缀编码if(memcmp(header[i].bits,bx,header[i].count)==0)/*此函数也为网络借鉴,memcmp函数此处的作用是比较bx的相应位是否与header[i].bits相同,若前header[i].count均相同,则返回0 */break;}strcpy(bx,bx+header[i].count);c=header[i].b;fwrite(&c,1,1,ofp);m++; //m用来统计解压缩后文件的长度if(m==flength) //检验是否与源文件长度匹配break;}fclose(ifp);fclose(ofp);printf("\n\t解压缩文件成功!\n");if(m==flength)printf("\t解压缩文件与原文件相同!\n\n");elseprintf("\t解压缩文件与原文件不同!\n\n");return;}int main(){int c;while(1){printf("\tHuffman前缀编码压缩&解压缩BY PB09000816 俞映洲\n");printf("\t1.压缩 2.解压缩0.退出\n");do{ //对用户输入进行容错处理printf("\n\t*请选择相应功能编号(0-2):");c=getch();printf("%c\n",c);if(c!='0' && c!='1' && c!='2'){printf("\t请检查您的输入在0~2之间!请再输入一遍!\n");}}while(c!='0' && c!='1' && c!='2');if(c=='1') //调用压缩子函数compress();else if(c=='2')uncompress(); //调用解压缩子函数else{exit(0);}return 0;}。