huffman编码译码实现文件的压缩与解压
huffman压缩存储和解压缩源代码(精品文档)
fst.open(filename, ifstream::in|ifstream::binary); wfile(fst, pssfile, Bytecode); fst.close(); pssfile.close(); system("pause"); }
void HuffmanCoding(array<huffman, m> &HT, array<string, n> &code, array<double, n> freq) { int j; unsigned char cj = 0; for (j = 0; j < n; ++j) { HT[cj].freq = freq[cj]; HT[cj].lchild = 0; HT[cj].rchild = 0; HT[cj].parent = 0; ++cj; } int i,s1, s2; for (i= n ; i <m; ++i) { myselect(HT, i, s1, s2); HT[s1].parent = i; HT[s2].parent = i; HT[i].lchild = s1; HT[i].rchild = s2; HT[i].freq = HT[s1].freq + HT[s2].freq; } for (i = 0; i < n; ++i) { string cd(n-1, '0'); int start = n-1; int k, f; for (k = i, f = HT[i].parent; f != 0; k = f, f = HT[f].parent) if (HT[f].lchild == k) cd[--start] = '0'; else cd[--start] = '1'; string cds(cd.substr(start, (n-1) - start)); code[i] = cds; }
利用哈夫曼编码实现压缩和解压缩
利用哈夫曼编码实现压缩和解压缩1.问题描述利用哈夫曼编码,实现压缩和解压缩的数据元素具有如下形式:结点:weight:存储结点的权值parent:是结点双亲在向量中的下标lchild:结点的左儿子向量下标rchild:结点右儿子向量下标bits:位串,存放编码ch:字符start:编码在位串中的起始位置文件操作记录:b:记录字符在数组中的位置count:字符出现频率(权值)lch、rch、parent:定义哈夫曼树指针变量bits[256]:定义存储哈夫曼编码的数组2.功能需求对于给定的一组字符,可以根据其权值进行哈夫曼编码,并能输出对应的哈夫曼树和哈夫曼编码;实现哈夫曼解码。
能够分析文件,统计文件中出现的字符,再对文件进行编码,实现文件的压缩和解压缩,能够对于文件的压缩,比例进行统计,能够打印文件。
3.实现要点(1)构造哈弗曼树过程中,首先将初始森林的各根结点的双亲和左、右儿子指针置-1;叶子在向量T的前n个分量中,构成初始森林的n个结点;对森林中的树进行n次合并,并产生n-1个新结点,依次放入向量T的第i个分量中。
(2)编码过程中,从叶子T[i]出发,利用双亲的指针找到双亲T[p];再根据T[p]的孩子指针可以知道T[i]是T[p]的左儿子还是右儿子,若是左儿子,则生成代码0,否则生成代码1;(3)在文件压缩和解压过程中,主要参考网上资料,每个步骤都基本理解,并注上了详细解析。
4.函数定义功能:输入权重,构造一棵哈弗曼树void huffman(hftree T){if(n<1 || n > m)return;int i,j,p1,p2;float small1,small2;//初始化cout<<"请输入叶子权重(5个):"<<endl;for(i=0; i<n; i++){T[i].parent = -1;T[i].lchild = T[i].rchild = -1;}//输入叶子权值for(i=0; i<n; i++){cin>>T[i].weight;}for(i=n; i<m; i++){p1 = p2 = -1;small1 = small2 = MAX_FLOAT;for(j=0; j<=i-1; j++){if(T[j].parent != -1)continue;if(T[j].weight < small1){small2 = small1;small1 = T[j].weight;p2 = p1;p1 = j;}else if(T[j].weight < small2){small2 = T[j].weight;p2 = j;}}T[p1].parent = T[p2].parent = i;T[i].parent=-1;T[i].lchild=p1;T[i].rchild=p2;T[i].weight=small1 + small2;}cout<<"创建成功!"<<endl;}功能:对哈弗曼树进行编码void encode(codelist codes, hftree T){int i,c,p,start;cout<<"请输入需要编码的字符(5个):"<<endl;for(i=0; i<n; i++){cin>>codes[i].ch;start=n;c=i;p=T[i].parent;while(p!=-1){start--;if(T[p].lchild==c) codes[i].bits[start]='0';else codes[i].bits[start]='1';c=p;p=T[p].parent;}codes[i].start=start;}cout<<"输入成功!:"<<endl;cout<<"编码表:"<<endl;for(int x=0; x<n; x++){cout<<codes[x].ch<<": ";for(int q=codes[x].start;q<n;q++) cout<<codes[x].bits[q];cout<<endl;}}函数功能:对哈弗曼树进行解码void decode(codelist codes,hftree T) {int i,c,p,b;int endflag;endflag=-1;i=m-1;while(cin>>b,b!=endflag){if(b==0) i=T[i].lchild;else i=T[i].rchild;if(T[i].lchild==-1){cout<<codes[i].ch;i=m-1;}}if(i!=m-1)cout<<"编码有错!\n";}功能:对文件进行压缩,统计压缩率void compress(){char filename[255],outputfile[255],buf[512];unsigned char c;long i,j,m,n,f;long min1,pt1,flength,length1,length2;double div;FILE *ifp,*ofp;cout<<"\t请您输入需要压缩的文件:";cin>>filename;ifp=fopen(filename,"rb");if(ifp==NULL){cout<<"\n\t文件打开失败!\n\n";return;}cout<<"\t请您输入压缩后的文件名:";cin>>outputfile;ofp=fopen(strcat(outputfile,".encode"),"wb");if(ofp==NULL){cout<<"\n\t压缩文件失败!\n\n";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;else header[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++) if(header[i].count==0) break;n=i; //外部叶子结点数为n个时,内部结点数为n-1,整个哈夫曼树的需要的结点数为2*n-1.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; //依据parent域值(结点层数)确定树中结点之间的关系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) //置左分支编码0{j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);//依次存储连接“0”“1”编码header[i].bits[0]='0';}else //置右分支编码1{j=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);fseek(ofp,8,SEEK_SET);buf[0]=0; //定义缓冲区,它的二进制表示00000000f=0;pt1=8;while(!feof(ifp)){c=fgetc(ifp);f++;for(i=0;i<n;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;else c=c<<1;}fwrite(&c,1,1,ofp);pt1++; //统计压缩后文件的长度strcpy(buf,buf+8); //一个字节一个字节拼接j=strlen(buf);}if(f==flength) break;}if(j>0) //对哈夫曼编码位操作进行压缩存储{strcat(buf,"00000000");for(i=0;i<8;i++){if(buf[i]=='1') c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp);pt1++;}fseek(ofp,4,SEEK_SET);fwrite(&pt1,sizeof(long),1,ofp);fseek(ofp,pt1,SEEK_SET);fwrite(&n,sizeof(long),1,ofp);for(i=0;i<n;i++){fwrite(&(header[i].b),1,1,ofp);c=strlen(header[i].bits);fwrite(&c,1,1,ofp);j=strlen(header[i].bits);if(j%8!=0) //若存储的位数不是8的倍数,则补0 {for(f=j%8;f<8;f++)strcat(header[i].bits,"0");}while(header[i].bits[0]!=0){c=0;for(j=0;j<8;j++) //字符的有效存储不超过8位,则对有效位数左移实现两字符编码的连接{if(header[i].bits[j]=='1') c=(c<<1)|1; //|1不改变原位置上的“0”“1”值else c=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;cout<<"\t请您输入需要解压缩的文件:";cin>>filename;ifp=fopen(strcat(filename,".encode"),"rb");if(ifp==NULL){cout<<"\n\t文件打开失败!\n";return;}cout<<"\t请您输入解压缩后的文件名:";cin>>outputfile;ofp=fopen(outputfile,"wb");if(ofp==NULL){cout<<"\n\t解压缩文件失败!\n";return;}fread(&flength,sizeof(long),1,ifp); //读取原文件长度,对文件进行定位fread(&f,sizeof(long),1,ifp);fseek(ifp,f,SEEK_SET);fread(&n,sizeof(long),1,ifp);for(i=0;i<n;i++){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;else m=p/8;for(j=0;j<m;j++){fread(&c,1,1,ifp);f=c;itoa(f,buf,2); //将f转换为二进制表示的字符串f=strlen(buf);for(l=8;l>f;l--){strcat(header[i].bits,"0");}strcat(header[i].bits,buf);}header[i].bits[p]=0;}for(i=0;i<n;i++) //根据哈夫曼编码的长短,对结点进行排序{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--) //在单字节内对相应位置补0{strcat(bx,"0");}strcat(bx,buf);}for(i=0;i<n;i++){if(memcmp(header[i].bits,bx,header[i].count)==0) break;}strcpy(bx,bx+header[i].count);c=header[i].b;fwrite(&c,1,1,ofp);m++; //统计解压缩后文件的长度if(m==flength) break; //flength是原文件长度}fclose(ifp);fclose(ofp);cout<<"\n\t解压缩文件成功!\n";if(m==flength) //对解压缩后文件和原文件相同性比较进行判断(根据文件大小)cout<<"\t解压缩文件与原文件相同!\n\n";else cout<<"\t解压缩文件与原文件不同!\n\n";return;}5、总结和体会本次大作业与C++大作业有所不同,主要是利用构造数据结构解决问题,C++大作业主要体现类和文件读写功能。
c++哈夫曼树的文件压缩解压程序全部代码及设计报告
#include <iostream>#include <fstream>#include <queue> //队列容器using namespace std;const int leaf = 256; //最多可能出现的不同字符数const long MAX = 99999999; //表示无穷大typedef struct HTnode{long weight; //记录结点的权值int parent; //记录结点的双亲结点位置int lchild; //结点的左孩子int rchild; //结点的右孩子int *code; //记录该结点的huffman编码int codelen; //记录该结点huffman编码的长度HTnode(){weight = MAX;parent = -1;lchild = -1;rchild = -1;codelen = 0;}}HTnode;class huffmanTree{public:huffmanTree();virtual ~huffmanTree();bool count(char *input); //统计各字符出现的次数,将其写入对应结点的权值void create(); //构造huffman树void code(); //计算每个字符的huffman编码void addbit(int bit); //压缩时对一个未满8个bit的byte中加入一个bitvoid resetbyte(); //将byte清空bool compress(char *input, char *output); //压缩函数成功执行返回true 失败falsebool decompress(char *input, char *output); //解压函数成功执行返回true 失败falsevoid compare(char *input, char *output); //将原文件与压缩后的文件比较private:int root; //记录根结点的位置int leafnum; //记录不同字符的个数HTnode HT[leaf*2-1]; //HTnode结构的数组,用来表示huffman树,树的最大结点个数不会超过leaf*2-1char byte; //压缩文件时用来缓冲bit的变量int bitsnum; //byte中bit的个数int lacknum; //压缩到最后byte中的bit不满8个时填充的0的个数};huffmanTree::huffmanTree(){//初始化成员变量root = 0;leafnum = 0;byte = 0;bitsnum = 0;lacknum = 0;}huffmanTree::~huffmanTree(){for(int i=0; i<leaf; i++){if(HT[i].codelen != 0)delete []HT[i].code;}}//统计各字符出现的次数bool huffmanTree::count(char *input){ifstream ifs;char c;ifs.open(input,ios::binary);if(!ifs){cout << "无法打开文件" << input << '!' << endl;return false;}while(ifs.get(c)){if(HT[c+128].weight==MAX){ //若该字符是第一次出现,先初始化权值HT[c+128].weight = 0;leafnum++;}HT[c+128].weight++; //权值+1}ifs.close();return true;}//选权值最小的两棵树组成新的数void huffmanTree::create(){for(int i=leaf; i<2*leaf-1; i++){int loc1=-1, loc2=-1;for(int j=0; j<i; j++){if(HT[j].parent != -1)continue;if(loc1==-1 || HT[j].weight < HT[loc1].weight){loc2 = loc1;loc1 = j;}else if(loc2==-1 || HT[j].weight < HT[loc2].weight)loc2 = j;}if(HT[loc1].weight==MAX || HT[loc2].weight==MAX || loc2==-1) //只剩一棵树,结束break;HT[i].weight = HT[loc1].weight + HT[loc2].weight;//为了减少压缩文件中需要写入的huffman树的信息,约定小标小的结点做为双亲结点的左孩子HT[i].lchild = loc1>loc2 ? loc2 : loc1;HT[i].rchild = loc1>loc2 ? loc1 : loc2;HT[loc1].parent = i; HT[loc2].parent = i;root = i;}}//计算每个字符的huffman编码void huffmanTree::code(){for(int i=0; i<leaf; i++){int len=0;int loc=i;while(HT[loc].parent!=-1){ //计算huffman编码长度len++;loc = HT[loc].parent;}HT[i].codelen = len;HT[i].code = new int[len];loc = i;for(int j=len-1; j>=0; j--){ //从后往前找,记录结点的huffman编码if(loc==HT[HT[loc].parent].lchild)HT[i].code[j] = 0;elseHT[i].code[j] = 1;loc = HT[loc].parent;}}}//压缩时对一个未满8个bit的byte中加入一个bitvoid huffmanTree::addbit(int bit){if(bit == 0)byte = byte << 1; //若新增的bit为0,则直接将byte按位左移elsebyte = ((byte << 1) | 1); //若新增的bit为1,先将byte按位左移,再与1按位或运算bitsnum++;}//将byte清空void huffmanTree::resetbyte(){byte = 0;bitsnum = 0;}//压缩函数成功执行返回true 失败falsebool huffmanTree::compress(char *input, char *output){if( !count(input) )return false;create();code();ifstream ifs;ofstream ofs;ifs.open(input,ios::binary);ofs.open(output,ios::binary);char c;if(!ifs){cout << "无法打开文件" << input << '!' << endl;return false;}if(!ofs){cout << "无法打开文件" << output << '!' << endl;return false;}ofs.put(0); //预留一个字符,等压缩完后在该位置写入不足一个byte的bit个数ofs.put(root-384); //将根节点的位置-384写入(为使该值不超过char的最大表示范围)for(int i=0; i<leaf*2-1; i++){ //写入每个结点的双亲结点位置if(HT[i].parent==-1) //若该节点没有双亲结点,则写入127(一个字节所能表示的最大值)ofs.put(127);else //否则将双亲结点的位置-384再写入(为使该值不超过char的最大表示范围)ofs.put(HT[i].parent-384);}while(ifs.get(c)){ //将字符的huffman编码并加入byte中int tmp = c+128;for(int i=0; i<HT[tmp].codelen; i++){addbit(HT[tmp].code[i]);if(bitsnum==8){ //若byte已满8位,则输出该byte并将byte清空ofs.put(byte);resetbyte();}}}if(bitsnum!=0){ //处理最后未满8个字符的byte,用0填充并记录填充的个数for(int i=bitsnum; i<8; i++){addbit(0);lacknum++;}ofs.put(byte);resetbyte();}ofs.seekp(0,ios::beg); //将写指针移动到文件开头ofs.put(lacknum); //写入最后一个字节缺失的bit个数ifs.close();ofs.close();return true;}//解压函数成功执行返回true 失败falsebool huffmanTree::decompress(char *input, char *output){queue<char> q;char c;ifstream ifs;ofstream ofs;ifs.open(input,ios::binary);ofs.open(output,ios::binary);if(!ifs){cout << "无法打开文件" << input << '!' << endl;return true;}if(!ofs){cout << "无法打开文件" << output << '!' << endl;return false;}ifs.get(c);lacknum = c; //读出最后一个字节缺失的bit个数ifs.get(c);root = c+384; //读出根结点的位置for(int i=0; i<leaf*2-1; i++){ //建立各结点之间的双亲孩子关系ifs.get(c);if(c==127)continue;else{HT[i].parent = c+384;if(HT[c+384].lchild==-1)HT[c+384].lchild = i;elseHT[c+384].rchild = i;}}int point = root;//为了方便处理最后一个可能有缺失bit的字节,先将读出的数据放入队列while(ifs.get(c))q.push(c);//还原文件过程while(q.size()>1){ //还未到最后一个字节c = q.front();for(int i=0; i<8; i++){if(int(c&128)==0){point = HT[point].lchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}else{point = HT[point].rchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}}q.pop();}c = q.front(); //最后一个字节for(i=0; i<8-lacknum; i++){if(int(c&128)==0){point = HT[point].lchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}else{point = HT[point].rchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}}q.pop();ifs.close();ofs.close();return true;}//将原文件与压缩后的文件比较void huffmanTree::compare(char *input, char *output){ifstream origin, compress;origin.open(input,ios::binary);compress.open(output,ios::binary);if(!origin){cout << "无法打开文件" << input << '!' << endl;return;}if(!compress){cout << "无法打开文件" << output << '!' << endl;return;}double total1=0, total2=0;char c;while(origin.get(c))total1++;while(compress.get(c))total2++;cout << "原文件大小:" << total1 << " Byte" << endl;cout << "压缩后大小:" << total2 << " Byte" << endl;cout << "压缩率:" << total2/total1*100 << '%' << endl;origin.close();compress.close();}void main(){int choice = 1;char input[255], output[255];huffmanTree h;while(choice){cout<<" ****************************************************"<<endl;cout<<" * 基于哈夫曼树的文件压缩/解压程序*"<<endl;cout<<" * *"<<endl;cout<<" * 1) 压缩*"<<endl;cout<<" * *"<<endl;cout<<" * 2) 解压*"<<endl;cout<<" * *"<<endl;cout<<" * 0) 退出*"<<endl;cout<<" * *"<<endl;cout<<" * 说明:请输入相应的操作序号*"<<endl;cout<<" ****************************************************"<<endl;cout<<"请选择:";cin >> choice;switch(choice){case 1:{cout << "请输入待压缩的文件名:";cin >> input;cout << "请输入压缩后的文件名:";cin >> output;if( press(input,output)){pare(input,output);cout<<"文件压缩成功!"<<endl;}else{cout<<"文件压缩失败!"<<endl;}}break;case 2:{cout << "请输入待解压的文件名:";cin >> input;cout << "请输入解压后的文件名:";cin >> output;if (h.decompress(input,output))cout<<"文件解压成功!"<<endl;elsecout<<"文件解压失败!"<<endl;}break;case 0:break;default:cout << "参数错误!请重新输入" << endl;}cout << endl;}}韶关学院计算机科学学院数据结构课程设计题目:基于哈夫曼树的文件压缩/解压程序学生姓名:曹键明学号:11115011018专业:计算机科学与技术班级:11级(1)班指导教师姓名及职称:陈正铭讲师起止时间:2013 年3 月——2013 年4 月1 需求分析1.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算法原理 Part 2 Huffm符号按照出现概率递减的顺序排列。 ) 的顺序排列。 2)将最下面的两个最小出现概率进行合并相加,得到的结果 )将最下面的两个最小出现概率进行合并相加, 作为新符号的出现概率。 作为新符号的出现概率。 3)重复进行步骤 和2直到概率相加的结果等于 为止。 直到概率相加的结果等于1为止 )重复进行步骤1和 直到概率相加的结果等于 为止。 4)在合并运算时,概率大的符号用编码 表示,概率小的符号 表示, )在合并运算时,概率大的符号用编码0表示 用编码1表示 表示。 用编码 表示。 5)记录下概率为 处到当前信号源符号之间的 ,1序列,从而 处到当前信号源符号之间的0, 序列 序列, )记录下概率为1处到当前信号源符号之间的 得到每个符号的编码。 得到每个符号的编码。
Part 1 Part 2 Part 3 Part 4
Part 1 文件压缩的现状
文件压缩的概念
一个较大的文件经压缩后, 一个较大的文件经压缩后,产生了另一个较小容量 的文件。而这个较小容量的文件, 的文件。而这个较小容量的文件,我们叫它是这些较大 容量的(可能一个或一个以上的文件) 压缩文件。 容量的(可能一个或一个以上的文件)的压缩文件。而 压缩此文件的过程称为文件压缩。 压缩此文件的过程称为文件压缩。
Huffman算法原理 Part 2 Huffman算法原理
Huffman编码简介
Huffman编码是一种可变长编码方式,是由美国 编码是一种可变长编码方式, 编码是一种可变长编码方式 数学家David Huffman创立的,是二叉树的一种特殊 创立的, 数学家 创立的 转化形式。 转化形式。 原理:将使用次数多的代码转换成长度较短的代码, 原理:将使用次数多的代码转换成长度较短的代码, 而使用次数少的可以使用较长的编码, 而使用次数少的可以使用较长的编码,并且保持编码的 唯一可解性。 唯一可解性。 原则:累计的(字符的统计数字 字符的编码长度)为 字符的统计数字*字符的编码长度 原则:累计的 字符的统计数字 字符的编码长度 为 最小,也就是权值(字符的统计数字 字符的编码长度)的 字符的统计数字*字符的编码长度 最小,也就是权值 字符的统计数字 字符的编码长度 的 和最小。 和最小。
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 文章结构本文主要介绍了哈夫曼编码的压缩与解压缩。
文章分为引言、正文和结论三个部分。
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树,依次选择权值最小的两个字符作为左右孩子,其和作为父结点的权值,依次进行下去,直到所有的字符结点都成为叶子结点。
数据结构实验报告利用Huffman编码对文件进行压缩解压
《数据结构》实验报告利用Huffman编码对文件进行压缩解压学生:XXX学号:XXXXXXXX联系:XXXXXX@(一)基本信息1.实验题目利用Huffman编码对文件进行压缩解压2.完成人(姓名、学号)姓名:XXX 学号:XXXXXXXX3.报告日期2007年12月12日星期二(二)实习内容简要描述1.实习目标学会对树的基本操作学会对文件进行操作利用Huffman编码对文件压缩解压2.实习要求实现最小堆模板类利用最小堆构建Huffman树实现Huffman编码和解码根据用户从键盘输入的报文文本,输出各字符的Huffman编码以及报文的编码根据用户从键盘输入一串报文文本,输出各字符的Huffman编码输出报文的Huffman编码及长度根据输入的Huffman编码,解码输出利用Huffman编码和解码对二进制文件的压缩和解压(三)报告主要内容1.设计思路开发环境:Microsoft Visual C++ 2005设计思路:1.设计Pack类储存字符的权值2.设计MinHeap模板类构建最小堆3.设计ExtBinTree模板类为带权二叉树4.设计Compress模板类以实现文件的压缩解压2.主要数据结构1.MinHeap.h: 头文件,包含MinHeap模板类的类界面以及定义;2.HuffmanTree.h:头文件,包含ExtBinTree模板类以及Compress模板类的类的的类界面以及定义3.main.cpp:调用上述头文件实现相关操作3.主要代码结构主要代码结构为见附件中各文件的代码注释4.主要代码段分析主要代码段分析,见附件中的代码注释(四)实习结果1.基本数据源程序代码行数:约800行完成该实习投入的时间:二十四小时(不包括写实验报告的时间)与其他同学讨论交流情况:感谢刘畅同学对程序的测试2.测试数据设计1.对屏幕输入字符进行Huffman编码2.根据Huffman树非唯一性,虽然和课件上有些许不同,但是还是正确的3.输入字符串:CASTCASTSATATATASA4.输出编码:A:0 C:110 S:111 T:105.输入霍夫曼编码:01110101101106.输出译码:ASATCC7.8.对”实验05.PPT”的压缩操作9.使用0秒(不足一秒按0秒计算),压缩率为56.1755%10.对”实验05.ppt.hfm”(即刚才生成的压缩文件)的解压操作11.使用0秒(不足一秒按0秒计算),解压后文件无异常12.对一个18M的EXE安装包前后进行压缩和解压操作,分别用时10秒和9秒3.测试结果分析A)程序运行的系统资源配置操作系统:Microsoft Windows XP Professional SP2CPU: AMD Athlon 3600+ 2.0GRAM: 1024M DDRII开发环境:Microsoft Visual C++ 2005B)对TXT文档进行压缩和解压后,通过WinMerge检验和原文件无差异C)对MP3和EXE文件压缩和解压后仍能正常使用D)对于中文名字和带有空格的路径均能正确无误识别E)文件名只能小于16个字符(包括后缀名),否则解压会出错(只预留了16个字节用于储存文件名)F)相对于不用文件块读写的程序,效率提高了三倍以上G)具有动态进度条,可以显示当前压缩和解压的进度百分比(当然消耗了一些系统资源)H)出错处理不够充分,特别是cin部分,如果误输入可能会造成死循环(五)实习体会1.实习过程中遇到问题及解决过程A)一开始时候的程序运行很慢,,压缩一个4M左右的文件需要七八秒,后来改用文件块缓存字节来读写后,压缩一个4M的文件只需要两秒左右的时间.本来是只想写一个进度条而已的,后来发现如果只读一个字节就判断一次进度条的话会很消耗系统资源,后来干脆麻烦点用文件块来缓存.不过至于一次缓存多少字节才能达到最好的效果还未知,现在设置的是一次缓存40KB 的数据B)本来一个一个字节读的时候对最后一个字节的操作基本没费什么劲,但是在文件块读写的时候就不是那么清晰明了了,后来经过仔细Debug,才找到错误的所在.许多问题都是这样C)对于中文名和带空格路径,用C++的fstream就不支持,但是C中的FILE*就支持,不知道为什么.还有C++中的fstream的成员函数read返回值很奇怪,不知道如何获取成功读入的项数.改用C中的FILE*文件指针后就解决掉了D)由于这次实验的各个步骤是一环套一环的,在哪里出错很难找得出来,所以这次实验调试消耗的时间特别多.最郁闷的一次错误是发现在取得字符C的第N位函数中,居然把0x40写成了0x30.有时候文件解压出来不对,但是又不清楚是压缩时候错了,还是解压时候错了,然后就要两个函数都要检查一遍.2. 实习体会和收获这次实验最大的特点在于难找错,很锻炼自己的Debug能力(六)自评成绩分数:90 //功能做得较齐全,程序的效率也不错(七)参考文献MSDN还有某网站上树的遍历算法(八)源程序见同一文件夹内.。
huffman编码,可实现压缩功能
#include<iostream>using namespace std;#include<iomanip>#include<fstream>#include<cstdlib>typedef struct Point{int parent;int weight;int left;int right;}POINT;class HUFFMAN{private:char Filech[258];POINT point[512]; //point[0]留作判断是否已经到文件的末尾了int root;char code[258][16];public:HUFFMANInit(char filename[]) //对huffman树进行一些初始化的操作{char ch;int i;for(i=0;i<512;i++){if(i<258)Filech[i]='\0';point[i].parent=point[i].weight=point[i].left=point[i].right=0;}Filech[0]=0;root=0;fstream FILE;FILE.open(filename,ios::in);if(!FILE){cout<<"打开文件失败"<<endl;exit(0);}while(!FILE.eof()){FILE.get(ch);if(FILE.fail())break;for(i=0;i<=strlen(Filech+1);i++){if(Filech[i]==ch){point[i].weight++;break;}}if(i==strlen(Filech+1)+1){Filech[i]=ch;point[i].weight++;}}buildhuffman();char s[2]="\0";gaincode(root,s);fstream FILEOUT; //将每个字符对应的二进制输出到文件中去FILEOUT.open("code.txt",ios::out);for(i=0;i<=strlen(Filech+1);i++){FILEOUT<<Filech[i]<<" "<<code[i]<<endl;}/* cout<<"各个字符在重新编码以后的码制:"<<endl; //输出各个字符在重新编码后的码制for(i=0;i<=strlen(Filech+1);i++)cout<<Filech[i]<<" "<<code[i]<<endl;for(i=1;i<=strlen(Filech+1);i++) //输出各个字符在统计之后的所占的比重cout<<i<<": "<<Filech[i]<<" "<<point[i].weight<<endl;*/}void buildhuffman() //构建huffman树{int compare[2][2];int i,j;int length;length=strlen(Filech+1);for(i=0;i<strlen(Filech+1);i++){compare[0][1]=compare[0][0]=0;compare[1][0]=compare[1][1]=0x7fffffff;for(j=0;j<=length;j++){if(!point[j].parent&&point[j].weight<compare[1][1]){if(point[j].weight<compare[1][0]){compare[0][1]=compare[0][0];compare[1][1]=compare[1][0];compare[0][0]=j;compare[1][0]=point[j].weight;}else{compare[0][1]=j;compare[1][1]=point[j].weight;}}}length++;point[compare[0][0]].parent=length;point[compare[0][1]].parent=length;point[length].left=compare[0][0];point[length].right=compare[0][1];point[length].weight=compare[1][0]+compare[1][1];}root=length;/* cout<<" 序号:父节点:左孩子:右孩子:比重:"<<endl;for(i=0;i<=root;i++)cout<<setw(4)<<i<<setw(8)<<point[i].parent<<setw(8)<<point[i].left<<setw(9)<<point[i].righ t<<setw(10)<<point[i].weight<<endl;*/}void gaincode(int address,char ch[]){if(point[address].left||point[address].right){int i=strlen(ch);char temp1[16];char temp2[16];strcpy(temp1,ch);strcpy(temp2,ch);temp1[i+1]='\0';temp1[i]='0';temp2[i+1]='\0';temp2[i]='1';gaincode(point[address].left,temp1);gaincode(point[address].right,temp2);}elsestrcpy(code[address],ch);}void jiami(char filename[],char fileoutname[]) //对文件进行编码后输出{fstream FileIn;fstream FileOut;FileIn.open(filename,ios::in);if(!FileIn){cout<<"打开文件失败"<<endl;exit(0);}FileOut.open(fileoutname,ios::out|ios::binary);if(!FileOut){cout<<"打开文件失败"<<endl;exit(0);}char ch;int num=0;int i=0,j,k;while(!FileIn.eof()){FileIn.get(ch);if(FileIn.fail())break;for(k=1;k<=strlen(Filech+1);k++)if(Filech[k]==ch)break;for(j=0;j<strlen(code[k]);j++){num<<=1;i++;if(code[k][j]=='1')num|=1;if(i==32){FileOut.write((char *)&num,sizeof(num));i=0;num=0;}}}for(j=0;j<strlen(code[0]);j++){num<<=1;i++;if(code[0][j]=='1')num|=1;if(i==32){FileOut.write((char *)&num,sizeof(num));i=0;num=0;}}if(i){num<<=32-i;FileOut.write((char *)&num,sizeof(num));}FileIn.close();FileOut.close();}void readFile(char filename[],char fileoutnmae[]) //将加密后的文件解密{fstream dataFileIn,dataFileOut;int i,j;int judge;int num;dataFileIn.open(filename,ios::in|ios::binary);if(!dataFileIn){cout<<"打开文件失败"<<endl;exit(0);}dataFileOut.open(fileoutnmae,ios::out);if(!dataFileOut){cout<<"打开文件失败"<<endl;exit(0);}j=root;while(!dataFileIn.eof()){dataFileIn.read((char *)&num,sizeof(num));if(dataFileIn.fail())break;for(i=31;i>=0;i--){if(point[j].left||point[j].right){judge=(num>>i)&1;if(judge)j=point[j].right;elsej=point[j].left;}else{i++;if(j==0)break;else{//cout<<Filech[j];dataFileOut.put(Filech[j]);j=root;}}}}}};。
C语言数据压缩与解压算法
C语言数据压缩与解压算法数据压缩和解压是计算机领域中常见的操作,通过对数据进行压缩可以减少存储空间占用和数据传输的带宽消耗。
而C语言作为一种广泛应用的编程语言,也提供了一些常用的数据压缩与解压算法实现。
一种常用的数据压缩算法是Huffman编码,它利用字符出现频率来构建不等长的编码字典,从而实现数据的压缩。
利用C语言实现Huffman编码可以简单地通过构建Huffman树来实现。
首先需要统计数据中每个字符的出现频率,然后根据频率构建Huffman树,生成每个字符的Huffman编码,最后将编码后的数据存储起来。
在解压时,根据Huffman编码和Huffman树将数据解压成原始数据。
另一种常用的数据压缩算法是Lempel-Ziv算法,它利用数据中的重复片段来实现压缩。
Lempel-Ziv算法分为LZ77和LZ78两种版本,其中LZ77通过滑动窗口来查找重复片段,LZ78则利用字典来存储重复片段。
在C语言中实现Lempel-Ziv算法需要对输入数据进行扫描,并利用匹配和存储的方法来实现压缩和解压。
除了Huffman编码和Lempel-Ziv算法,还有其他许多数据压缩算法可以在C语言中实现,如Run-Length编码、Arithmetic编码等。
这些算法各有特点,适用于不同类型的数据压缩需求。
需要注意的是,对于数据压缩算法的实现,除了算法本身的正确性和高效性外,还需要考虑压缩比、压缩速度、解压速度等指标来评估算法的优劣。
在实际应用中,需要根据具体的场景和需求选择合适的数据压缩算法。
总之,C语言提供了丰富的数据压缩与解压算法实现方式,开发人员可以根据具体需求选择合适的算法来提升数据传输效率和节约存储空间。
通过熟练掌握各种数据压缩算法,可以更好地应对不同的数据压缩需求,提高计算机系统的性能和效率。
文件的压缩与解压huffman算法功能实现
文件的压缩与解压──huffman算法功能实现摘要:压缩的实质是数字变换,在多媒体信息中包含大量冗余的信息,把这些余冗的信息去掉,就是实现了压缩;解压是由于计算机处理的信息是以二进制数的形式表示的,因此压缩软件就是把二进制信息中相同的字符串以特殊字符标记来达到压缩的目的。
关键词:压缩;解压缩;需求分析;概要设计;详细设计;测试Abstract:Compression is the essence of digital transformation, contains a large amount of redundant information in the multimedia information, the residual redundancy information, is to implement the compression; Decompression is due to the computer processing of information in the form of a binary number, so the compression software is binary information in the same string tag as special characters to achieve the purpose of compression.Key words:Compression; The decompression; Demand analysis; The profile design; The detailed design; Test;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<<"正在压缩。
实验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]中是双亲结点索引。
霍夫曼编码对英文文本的压缩和解压缩
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',那么将其左移一位,看起来没什么变化。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构课程设计题目名称: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.1.1 字符的统计用结构体huffchar来存放文件字符的信息。
其中有文件中不同字符出现的种类Count、字符data。
struct huffchar{//存放读入字符的类;int Count;//字符出现的个数;char data;//字符;};函数实现:bool char_judge(char c)//判断字符出现的函数;void char_add(char c)//添加新出现的字符;void read_file_count() //文件的读取2.1.2 huffman树节点的设计用结构体huff_tree来存储结点信息,其中有成员频率weight、父亲节点parent、左儿子节点lchild、右儿子节点rchild。
struct huff_tree{//huffman树结点定义;int parent;int lchild;int rchild;int weight;};函数实现:void creathuffman() //构造huffman树的函数2.2构造huffman编码2.2.1 huffman编码的设计用结构体huffcode来存储每个字符的编码。
其中有编码数组bits、起始编码start、频数count、编码对应的字符c。
struct huffcode{//结构体string bits[100];//存放解码;int start;//int count;//频数string c;//存放字符;};函数实现:void huffmancode()//编码函数2.3 压缩文件与解压文件的实现将压缩的文件根据huffman树进行编码,存入新的文件(huffman1.txt)中,将huffman.txt 按照huffman编码对应的字符解压回来,但是这样的文件是压缩不了的,当时用了一个30kb 的文件压缩后竟然成了119kb,导致这样的问题是因为一个字符对应几个二进制数字,然而一个二进制文字也是占一个字节,这就导致了压缩后的文件变大。
处理的方法将huffman1.txt文件中的二进制编码7位7位的存成一个ascII值存入新的文件,这样就实现了真正打压缩。
解压的时候将文件中的ascII值重新弄成二进制,不够7位的前边补0,例如ascII值为99的二进制位100011这是6位的ascII码所以再前边补一个0那么99的二进制就变成0100011。
接下来就利用huffman编码将这个文件重新译码过来。
函数实现:void code_huffman_file()//编码后的二进制码存入文件void code_file_out()//将编码过的文件恢复;void evaluating()//比较文件压缩的比例void change()//将二进制编码变成ascII码void yichu()//将ascII翻译成二进制码恢复文件三、执行效果本算法有一个初始文件为huffman.txt。
为一篇小说,大小为32KB3.1界面3.2每个字符的编码3.3操作部分3.4文件效果huffman为初始文件大小为30KB,huffman1为二进制编码文件大小为130KB,huffman2文件为解压后的文件大小为30KB,huffman3.为真正压缩后的文件大小为19KB,huffman 为huffman3文件解压后的文件大小为30KB,与huffman文件内容相同。
标记的文件就是压缩后的huffman3文件。
四、源程序:#include <iostream>#include <fstream>#include <string>#include <iomanip>#include <cstdio>#include <algorithm>#include <queue>using namespace std;string remfile[3100000];//存放原文件字符的数组;char strstr[1500000];int remcount=0;//记录元素个数;float bitecount=0;//记录二进制码的个数;/****************************************************************/ struct huffchar{//存放读入字符的类;int Count;//字符出现的个数;char data;//字符;};int Count=1;//记录huff数组中字符实际出现的个数;huffchar huff[1000];//类的对象;/****************************************************************//*文件读入部分和统计字符出现的频率*/bool char_judge(char c)//判断字符出现的函数;{for(int i=1;i<=Count;i++)if(huff[i].data==c){huff[i].Count++;return true;}//如果出现过,出现的频数加1;return false;}void char_add(char c)//添加新出现的字符;{huff[Count].data=c;huff[Count++].Count++;//个数增加,}//文件的读取void read_file_count(){char c;ifstream infile;infile.open("huffman.txt");//打开huffman.txt文件;if(!infile)//检查文件是否打开。
{cerr<<"不能打开huffman.txt文件";//输出文件未打开的标志。
exit(0);}cout<<"读入的文件中的内容为:"<<endl;while((c=infile.get())!=EOF){remfile[++remcount]=c;if(!char_judge(c))char_add(c);}cout<<endl;}/******************文件读入和统计字符出现频率部分结束**************/ /******************************************************************//******************构造huffman树程序部分***************************/ struct huff_tree{//huffman树结点定义;int parent;int lchild;int rchild;int weight;};int sum;//huffman树中结点的个数;huff_tree huffman[1000];void creathuffman()//构造huffman树的函数{int min1,min2;//指示权值最小;int loc1,loc2;//指向权值最小的两个数的位置;for(int i=1;i<=sum;i++){ //对huffman树的结点进行初始化;huffman[i].parent=0;huffman[i].lchild=0;huffman[i].rchild=0;huffman[i].weight=0;}for(int ii=1;ii<Count;ii++)//将权值赋给huffman[].weight;huffman[ii].weight=huff[ii].Count;sum=2*Count-3;for(int j=Count;j<=sum;j++){loc1=loc2=0;//权值最小的min1=min2=2000000;for(int k=1;k<=j-1;k++)//求权值最小的两个地址;if(huffman[k].parent==0)if(huffman[k].weight<=min1){min2=min1;min1=huffman[k].weight;loc2=loc1;loc1=k;}elseif(huffman[k].weight<=min2){min2=huffman[k].weight;loc2=k;}////////////将求出的两个权值最小的结点合并为新的结点,并将新的结点存入数组中huffman[loc1].parent=j;huffman[loc2].parent=j;huffman[j].lchild=loc1;huffman[j].rchild=loc2;huffman[j].weight=huffman[loc1].weight+huffman[loc2].weight;}}/*******************************构造huffman树的程序部分结束********************************//*************************************huffman编码开始**************************************/struct huffcode{//结构体string bits[100];//存放解码;int start;//int count;//频数string c;//存放字符;};huffcode hcode[100];void huffmancode()//编码函数{int rem,p;int count1=0;for(int y=1;y<=Count;y++){//编码部分;rem=y;hcode[y].start=sum;hcode[y].c=huff[y].data;p=huffman[y].parent;while(p!=0){if(huffman[p].lchild==rem)hcode[y].bits[++count1]='0';else hcode[y].bits[++count1]='1';rem=p;p=huffman[p].parent;}hcode[y].count=count1;count1=0;}cout<<"字符以及其编码:"<<endl;for(int t=1;t<=Count;t++)//输出所编的码;{cout<<"字符"<<hcode[t].c<<";编码:";int r=hcode[t].count;while(r)cout<<hcode[t].bits[r--];cout<<endl;}}/****************************************************************************** ******/string str;void code_huffman_file(){ofstream fp;cout<<"请输入文件名"<<endl<<"例如:huffman1.txt"<<endl;cout<<"该文件用来存放编码后的文件即压缩文件"<<endl;cin>>str;fp.open(str.c_str());if(!fp)//检查文件是否打开。