C语言大作业(基于哈夫曼树的压缩程序)
利用哈夫曼编码实现压缩和解压缩
利用哈夫曼编码实现压缩和解压缩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语言实现文件压缩
《用哈夫曼编码实现文件压缩》实验报告课程名称数据结构B实验学期2012 至2013 学年第一学期学生所在系部计算机学院年级2011 专业班级信管B111学生姓名学号任课教师兰芸实验成绩用哈夫曼编码实现文件压缩1、了解文件的概念。
2、掌握线性链表的插入、删除等算法。
3、掌握Huffman树的概念及构造方法。
4、掌握二叉树的存储结构及遍历算法。
5、利用Huffman树及Huffman编码,掌握实现文件压缩的一般原理。
微型计算机、Windows 系列操作系统、Visual C++6.0软件根据ascii码文件中各ascii字符出现的频率情况创建Haffman树,再将各字符对应的哈夫曼编码写入文件中,实现文件压缩。
(1)构造Hufffman树的方法—Hufffman算法构造Huffman树步骤:I.根据给定的n个权值{w1,w2,……wn},构造n棵只有根结点的二叉树,令起权值为wj。
II.在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和。
III.在森林中删除这两棵树,同时将新得到的二叉树加入森林中。
IV.重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树。
(2)Huffman编码:数据通信用的二进制编码思想:根据字符出现频率编码,使电文总长最短编码:根据字符出现频率构造Huffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列。
(3) 解压根据存放在文件中的编码表和文件压缩后的编码,进行一对一的翻译过程。
压缩的代码#include <stdio.h>#include <string.h>#include <stdlib.h>#include <conio.h>struct head{unsigned char b; /*the charactor*/long count; /*the frequency*/long parent,lch,rch; /*make a tree*/char bits[256]; /*the haffuman code*/}header[512],tmp;void yasuo() /*压缩*/{char filename[255],outputfile[255],buf[512];unsigned char c; char wenjianming[255];long i,j,m,n,f;long min1,pt1,flength;FILE *ifp,*ofp;printf("输入文件地址及文件名:");gets(filename);ifp=fopen(filename,"rb"); /*打开源文件*/while(ifp==NULL){ printf("打开文件出错\n");printf("重新输入文件地址及文件名");gets(filename);ifp=fopen(filename,"rb");}printf("输入压缩后的文件地址和文件名及后缀:");gets(wenjianming);ofp=fopen(wenjianming,"wb"); /*创建并打开目的文件*/ while(ofp==NULL){printf("重新输入压缩后的文件地址和文件名及后缀:");ofp=fopen(wenjianming,"wb");}flength=0;while(!feof(ifp)) /*读取ifp文件*/{fread(&c,1,1,ifp); /*按位读取*/header[c].count++;flength++;}flength-1;header[c].count-1; /*读取文件结束*/for(i=0;i<512;i++) /*构造哈弗曼树*/{if(header[i].count!=0)header[i].b=(unsigned char)i;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++)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;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;while(header[f].parent!=-1){j=f;f=header[f].parent;if(header[f].lch==j){j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);header[i].bits[0]='0';}else{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); /*把文件指针指向文件的开头*/fwrite(&flength,sizeof(int),1,ofp); //把哈弗曼代码写入ofp文件fseek(ofp,8,SEEK_SET);buf[0]=0;f=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); //把header[i].bits所指字符串添加到buf结尾处j=strlen(buf); //计算字符串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); /*fseek 用于二进制方式打开的文件,移动文件读写指针位置.第一个是文件流,第3个是指针零点位置,第2个是把指针移动到的地点. */ 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) /*按八位读取*/{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++){if(header[i].bits[j]=='1') c=(c<<1)|1;else c=c<<1;}strcpy(header[i].bits,header[i].bits+8); /*把从header[i].bits+8地址开始且含有NULL结束符的字符串赋值到以header[i].bits开始的地址空间*/fwrite(&c,1,1,ofp);}}fclose(ifp);fclose(ofp);printf("压缩成功\n");}void main() /*主函数*/ {printf("输入a开始压缩\n");printf("输入b结束压缩\n");while(1){char c;c=getch();if(c=='a')yasuo();else{ if(c=='b')return;}}}压缩的图解解压的代码#include <stdio.h>#include <string.h>#include <stdlib.h>#include <conio.h>struct headunsigned char b; /*the charactor*/long count; /*the frequency*/long parent,lch,rch; /*make a tree*/char bits[256]; /*the haffuman code*/}header[512],tmp;void jieya() /*解压*/{char filename[255],outputfile[255],buf[255],bx[255];unsigned char c; char wenjianming[255];long i,j,m,n,f,p,l;long flength;FILE *ifp,*ofp;printf("要解压的文件:");gets(filename);ifp=fopen(filename,"rb"); /*打开源文件*/while(ifp==NULL){ printf("打开文件出错\n");printf("重新输入文件地址及文件名");gets(filename);ifp=fopen(filename,"rb");}printf("输入解压后的文件地址和文件名及后缀:");gets(wenjianming);ofp=fopen(wenjianming,"wb"); /*创建并打开目的文件*/ while(ofp==NULL){ofp=fopen("d:\\123\\解压的文件.txt","wb");}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=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--){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;}fclose(ifp);fclose(ofp);printf("解压成功\n");return;}void main() /*主函数*/{printf("输入a开始解压\n");printf("输入b结束解压\n");while(1){char c;c=getch();if(c=='a')jieya();else{ if(c=='b')return;}}}压缩前的文件夹中的内容压缩后的文件夹中的内容解压后文件夹中的内容八、教师评语:教师评价评定项目A B C D评定项目A B C D 算法正确界面美观,布局合理程序结构合理操作熟练语法、语义正确解析完整实验结果正确文字流畅报告规范题解正确其他:评价教师签名:年月日。
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课题背景及意义近年来,随着计算机技术的发展,多媒体计算机技术、计算机网络技术以及现代多媒体通信技术正在向着信息化、高速化、智能化迅速发展。
哈夫曼文本压缩C语言实现
}
//构造完毕
Lptr pop(int last)
{
int i;
Lptr head,p;
head=p=new Lnode;
for(i=0;i<last;i++)
{
p->c=co[i];
p->next=new Lnode;
p=>next;
}
p->c=-1;
{
printf("文件打开错误!\n");
exit(0);
}
while(!feof(fp))
{
p->c=fgetc(fp)&0xFF;
p->next=new Lnode;
p=p->next;
/*putchar(ch[i]);*/
i++;
}
putchar(10);
fclose(fp);
return(i-1);
j++;
}while(j%8);
return(y);
}
int restoretree(snode ch2[],int num2,int num1,char outfile[])
{
FILE*out1;
int i=0,j=0;
if((out1=fopen(outfile,"wb"))==NULL)
{
printf("文件打开错误!\n");
printf("还原结果:");
}
int putdata(Lptr strhead)//输入函数
{
哈夫曼编码压缩c语言
哈夫曼编码压缩c语言
哈夫曼编码是一种常用的数据压缩算法,也是一种非常基础的数
据结构。
它以出现频率为基础来构建编码表,将出现频率较高的字符
用较短的编码来表示,而出现频率较低的字符用较长的编码表示,从
而实现对数据的高效压缩。
在哈夫曼编码中,编码表的构建是非常关键的一步。
它需要统计
每个字符在数据中出现的频率,并且构建一棵哈夫曼树,从而得到每
个字符的编码。
具体的,哈夫曼树是一棵满足以下条件的二叉树:叶
节点代表每个字符,非叶节点代表编码,树的根节点代表整个编码表。
在哈夫曼树中,左子树代表编码的“0”位,右子树代表编码的“1”位。
一旦构建好了哈夫曼树,数据的压缩就非常简单了。
对于每个字符,将其对应的编码拼接起来即可。
因为哈夫曼编码是以出现频率为
基础的,所以出现频率较高的字符的编码较短,其压缩后的数据也较短;而出现频率较低的字符的编码较长,其压缩后的数据也较长。
这
样就实现了对数据的高效压缩。
在实际应用中,哈夫曼编码被广泛应用于数据压缩。
比如在通信
领域,它常被用于网络传输和存储;在计算机领域,它被用于文件压
缩和解压缩。
在这些应用中,哈夫曼编码可以帮助我们节省大量的存
储空间和网络带宽,从而提高通信和存储的效率。
总之,哈夫曼编码是一种非常重要的算法,在数据压缩和存储领
域都有着广泛的应用。
掌握哈夫曼编码可以帮助我们更好地理解数据
压缩的原理和实现方法,从而开发出更加高效的数据压缩算法和应用。
用c语言编程实现用霍夫曼编码进行的数据压缩
用c语言编程实现用霍夫曼编码进行的数据压缩,需要完成以下功能:输入一个文档,声频,图像或者视频,能够对输入的这个原数据用霍夫曼编码的方式进行编码,压缩.请注意我的输入要求.#include <stdio.h>#include <string.h>#include <stdlib.h>#include <malloc.h>#include <conio.h>typedef struct {unsigned int weight;unsigned int parent,lchild,rchild;} HTNode,*HuffmanTree;typedef char **HuffmanCode;typedef struct {unsigned int s1;unsigned int s2;} MinCode;void Error(char *message);HuffmanCode HuffmanCoding(HuffmanTree HT,HuffmanCode HC,unsigned int*w,unsigned int n);MinCode Select(HuffmanTree HT,unsigned int n);void Error(char *message){clrscr();fprintf(stderr,"Error:%s\n",message);exit(1);}HuffmanCode HuffmanCoding(HuffmanTree HT,HuffmanCode HC,unsigned int*w,unsigned int n){unsigned int i,s1=0,s2=0;HuffmanTree p;char *cd;unsigned int f,c,start,m;MinCode min;if(n<=1) Error("Code too small!");m=2*n-1;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); for(p=HT,i=0;i<=n;i++,p++,w++){p->weight=*w;p->parent=0;p->lchild=0;p->rchild=0;}for(;i<=m;i++,p++){p->weight=0;p->parent=0;p->lchild=0;p->rchild=0;}for(i=n+1;i<=m;i++){min=Select(HT,i-1);s1=min.s1;s2=min.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;}printf("HT List:\n");printf("Number\t\tweight\t\tparent\t\tlchild\t\trchild\n"); for(i=1;i<=m;i++)printf("%d\t\t%d\t\t%d\t\t%d\t\t%d\n",i,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild); HC=(HuffmanCode)malloc((n+1)*sizeof(char *));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';else cd[--start]='1';HC[i]=(char *)malloc((n-start)*sizeof(char *));strcpy(HC[i],&cd[start]);}free(cd);return HC;}MinCode Select(HuffmanTree HT,unsigned int n) {unsigned int min,secmin;unsigned int temp;unsigned int i,s1,s2,tempi;MinCode code;s1=1;s2=1;for(i=1;i<=n;i++)if(HT[i].parent==0){min=HT[i].weight;s1=i;break;}tempi=i++;for(;i<=n;i++)if(HT[i].weight<min&&HT[i].parent==0){min=HT[i].weight;s1=i;}for(i=tempi;i<=n;i++)if(HT[i].parent==0&&i!=s1){secmin=HT[i].weight;s2=i;break;}for(i=1;i<=n;i++)if(HT[i].weight<secmin&&i!=s1&&HT[i].parent==0) {secmin=HT[i].weight;s2=i;}if(s1>s2){temp=s1;s1=s2;s2=temp;}code.s1=s1;code.s2=s2;return code;}void main(){HuffmanTree HT=NULL;HuffmanCode HC=NULL;unsigned int *w=NULL;unsigned int i,n;clrscr();printf("Input n:\n");scanf("%d",&n);w=(unsigned int *)malloc((n+1)*sizeof(unsigned int *));w[0]=0;printf("Enter weight:\n");for(i=1;i<=n;i++){printf("w[%d]=",i);scanf("%d",&w[i]);}HC=HuffmanCoding(HT,HC,w,n);printf("HuffmanCode:\n");printf("Number\t\tWeight\t\tCode\n");for(i=1;i<=n;i++)printf("%d\t\t%d\t\t%s\n",i,w[i],HC[i]);}程序运行:首先用户先输入一个数n,以实现n个节点的Huffman Tree 之后输入权值w[1]~w[n],注意是unsigned int型数值。
基于哈弗曼编码的数据压缩C语言实现
摘要数据压缩技术是一项重要实用的信息技术。
信息时代的到来,信息量迅速增长,使得数据压缩也显得越来越重要。
数据压缩有多种编码方法,大致可分为无损压缩编码和有损压缩编码。
其中,Huffman 编码是第一个实用的无损压缩编码技术,经过了多年的不断改进,已经形成了系统的理论和方法。
目前主要有两种类型的Huffman编码方式,即静态Huffman编码和动态Huffman编码。
Huffman编码有着广泛的应用,但是不再是压缩算法的全部,而是被当作最终的编码方法。
本文首先介绍了数据压缩的发展历史和数据压缩的基本原理,然后介绍了Huffman编码的原理和算法,并用C语言来实现哈夫曼算法。
关键词: 数据压缩,Huffman编码,动态Huffman编码ABSTRACTData compression is a very important and practical information technology. In the information age, data compression technology becomes more and more important because of the rapid growth of the quantity of information. There are many coding methods of data compression, substantially, can be divided into lossless and lossy compression coding. Huffman coding is the first practical lossy compression coding technology. After many years of continuous improvement, it has formed a system of the theory and method. Currently there are two major types of Huffman coding, static Huffman coding and dynamic Huffman coding. Huffman coding has extensive applications, which is no longer the all of the compression algorithm, but to be the final coding method.Firstly, this paper introduces the history and basic principles and methods of data compression, then introduces the theory and algorithm of huffman coding and using C language to implement it.KEY WORDS: data compression, Huffman coding, dynamic Huffman coding第一章绪论数据压缩技术早在计算机技术的萌芽时期就已经受到了足够的重视,信息论的产生和发展, 也使数据压缩技术由热门话题演变成真正的应用技术。
压缩字符串c语言哈夫曼树
压缩字符串c语言哈夫曼树哈夫曼树(Huffman Tree)是用于数据压缩的一种算法,其可以将一段具有重复出现频率的字符串压缩,并在解压时将其解压回原来的字符串内容。
在本文中,我们将学习如何在C语言中实现哈夫曼树来压缩字符串。
1. 哈夫曼树的原理哈夫曼树是一种特殊的二叉树,由带有权重的节点组成。
权重越大的节点离根节点越近。
具有相同权重的节点可以位于同一层中。
在一个哈夫曼树中,每个叶子节点都代表字符串中的一个字符,而从根节点到该叶子节点的路径上的二进制值表示该字符的编码。
使用哈夫曼树对字符串进行压缩的基本思路是:首先统计每个字符在字符串中出现的次数,然后根据字符出现的次数构建哈夫曼树。
最后,根据哈夫曼树的叶子节点和根节点到叶子节点路径的编码表来代替原始的字符编码。
这样,我们就可以把原始的字符串替换成用更短的编码来表示的压缩版本。
2. 如何实现哈夫曼树的构建为了构建我们的哈夫曼树,我们需要遵循以下步骤:步骤1:创建一个节点,将所有的字符和它们出现的频率存储在节点中。
步骤2:将这些节点按照频率排序步骤3:每次取出最小的两个节点,创建一个新的节点作为它们的父亲,并把这两个节点作为新节点的左子节点和右子节点。
步骤4:将新节点的频率设为左右子节点的频率之和。
步骤5:将新节点插入到已排序的数组中,并重复步骤2-4,直到只剩下一个节点。
这个最后的节点就是哈夫曼树根节点。
3. 根据哈夫曼树构建编码表通过哈夫曼树,我们可以得出每个字符的编码。
在哈弗曼树中,左子树的节点得到的编码是以0开头的,右子树的节点得到的编码是以1开头的。
所以如果我们想要得到一个字符的编码,就需要从根节点开始遍历哈夫曼树,每遇到一个节点就将其编码加到编码表中,直到遇到叶子节点。
4. 压缩字符串知道了如何构建哈夫曼树和如何构建编码表,现在我们可以用编码表来将字符串压缩。
首先,我们需要记录每个字符出现的次数,并对这些次数进行哈夫曼树构建。
接下来,我们需要遍历字符串,使用编码表将每个字符替换为它的编码。
哈夫曼树与文件解压压缩C言代码
哈夫曼树与文件解压压缩C言代码1.问题描述哈弗曼树的编码与译码—功能:实现对任何类型文件的压缩与解码—输入:源文件,压缩文件—输出:解码正确性判定,统计压缩率、编码与解码速度—要求:使用边编码边统计符号概率的方法(自适应Huffman编码)和事先统计概率的方法(静态Huffman编码)2.1程序清单程序书签:1.main函数2.压缩函数3.select函数4.encode函数5.解压函数#include <stdio.h>#include <string.h>#include <stdlib.h>#include <conio.h>#include <time.h>struct node{long weight; //权值unsigned char ch;//字符int parent,lchild,rchild;char code[256];//编码的位数最多为256位int CodeLength;//编码长度}hfmnode[512];void compress();void uncompress();//主函数void main(){int choice;printf("请选择1~3:\n");printf("1.压缩文件\n");printf("2.解压文件\n");printf("3.退出!\n");scanf("%d",&choice);if(choice==1)compress();else if(choice==2)uncompress();else if(choice==3)return;else printf("输入错误!");}//压缩函数void compress(){int i,j;char infile[20],outfile[20];FILE *ifp,*ofp;unsigned char c;//long FileLength,filelength=0;int n,m;//叶子数和结点数int s1,s2; //权值最小的两个结点的标号char codes[256];long sumlength=0;float rate,speed;int count=0;clock_t start1, start2,finish1,finish2;double duration1,duration2;void encode(struct node *nodep,int n);//编码函数int select(struct node *nodep,int pose);//用于建哈弗曼树中选择权值最小的结点的函数printf("请输入要压缩的文件名:");scanf("%s",infile);ifp=fopen(infile,"rb");if(ifp==NULL){printf("文件名输入错误,文件不存在!\n");return;}printf("请输入目标文件名:");scanf("%s",outfile);ofp=fopen(outfile,"wb");if(ofp==NULL){printf("文件名输入错误,文件不存在!\n");return;}start1=clock() ;//开始计时1//统计文件中字符的种类以及各类字符的个数//先用字符的ASCII码值代替结点下标FileLength=0;while(!feof(ifp)){fread(&c,1,1,ifp);hfmnode[c].weight++;FileLength++;}FileLength--; //文件中最后一个字符的个数会多统计一次,所以要减一hfmnode[c].weight--;//再将ASCII转换为字符存入到结点的ch成员里,同时给双亲、孩子赋初值-1n=0;for(i=0;i<256;i++)if(hfmnode[i].weight!=0){hfmnode[i].ch=(unsigned char)i;n++;//叶子数hfmnode[i].lchild=hfmnode[i].rchild=hfmnode[i].parent=-1;}m=2*n-1;//哈弗曼树结点总数j=0;for(i=0;i<256;i++)//去掉权值为0的结点if(hfmnode[i].weight!=0){hfmnode[j]=hfmnode[i];j++;}for(i=n;i<m;i++)//初始化根结点{hfmnode[i].lchild=hfmnode[i].rchild=-1;hfmnode[i].parent=-1;}//建立哈弗曼树for(i=n;i<m;i++){s1=select(hfmnode,i-1);hfmnode[i].lchild=s1;hfmnode[s1].parent=i;s2=select(hfmnode,i-1);hfmnode[i].rchild=s2;hfmnode[s2].parent=i;hfmnode[i].weight=hfmnode[s1].weight+hfmnode[s2].weight;}//编码encode(hfmnode,n);finish1=clock();duration1=(double)(finish1- start1) / CLOCKS_PER_SEC;/*printf( "哈弗曼树编码用时为:%f seconds\n", duration1 );*/printf("编码完成,是否查看编码信息: y or n?\n");c=getch();if(c=='y'){ printf("\n");printf("叶子数为%d,结点数为%d\n",n,m);for(i=0;i<n;i++)printf("%d号叶子结点的权值为:%ld,双亲为:%d,左右孩子:%d,编码为:%s\n",i,hfmnode[i].weight,hfmnode[i].parent,hfmnode[i].lchild,hfmnode[i].code);}start2=clock() ;//开始计时2fseek(ifp,0,SEEK_SET);//将ifp指针移到文件开头位置fwrite(&FileLength,4,1,ofp);//将FileLength写入目标文件的前4个字节的位置fseek(ofp,8,SEEK_SET);//再将目标文件指针ofp移到距文件开头8个字节位置codes[0]=0;//将编码信息写入目标文件while(!feof(ifp)){fread(&c,1,1,ifp);filelength++;for(i=0;i<n;i++)if(c==hfmnode[i].ch) break; //ch必须也为unsigned 型strcat(codes,hfmnode[i].code);while(strlen(codes)>=8){for(i=0;i<8;i++)//将codes的前8位01代码表示的字符存入c{if(codes[i]=='1')c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp); //将新的字符写入目标文件sumlength++;strcpy(codes,codes+8);//更新codes的值}if(filelength==FileLength) break;}//再将剩余的不足8位的01代码补全8位,继续写入if(strlen(codes)>0){strcat(codes,"00000000");for(i=0;i<8;i++){if(codes[i]=='1')c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp);sumlength++;}sumlength+=8;printf("编码区总长为:%ld个字节\n",sumlength-8);//将sumlength和n的值写入目标文件,为的是方便解压fseek(ofp,4,SEEK_SET);fwrite(&sumlength,4,1,ofp);//把sumlength写进目标文件的第5-8个字节里fseek(ofp,sumlength,SEEK_SET);fwrite(&n,4,1,ofp);//把叶子数n写进编码段后面的4个字节的位置//为方便解压,把编码信息存入n后面的位置//存储方式为:n*(字符值(1个字节)+该字符的01编码的位数(1个字节)+编码(字节数不确定,用count来计算总值))for(i=0;i<n;i++){fwrite(&(hfmnode[i].ch),1,1,ofp);c=hfmnode[i].CodeLength;//编码最长为256位,因此只需用一个字节存储fwrite(&c,1,1,ofp);//写入字符的编码if(hfmnode[i].CodeLength%8!=0)for(j=hfmnode[i].CodeLength%8;j<8;j++)//把编码不足8位的在低位补0,赋值给C,再把C写入strcat(hfmnode[i].code,"0");while(hfmnode[i].code[0]!=0)//开始存入编码,每8位二进制数存入一个字节{c=0;for(j=0;j<8;j++){if(hfmnode[i].code[j]=='1')c=(c<<1)|1;else c=c<<1;}strcpy(hfmnode[i].code,hfmnode[i].code+8);//编码前移8位,继续存入编码count++; //编码占的字节数的总值fwrite(&c,1,1,ofp);}}printf("\n");finish2=clock();duration2=(double)(finish2- start2) / CLOCKS_PER_SEC;/*printf( "写入目标文件用时为:%f seconds\n", duration2);*/printf( "压缩用时为:%f seconds\n", duration1+duration2);speed=(float)FileLength/(duration1+duration2)/1000;printf("\n压缩速率为:%5.2f KB/S\n",speed);printf("\n");printf("源文件长度为:%ld个字节\n",FileLength);sumlength=sumlength+4+n*2+count; //计算压缩后文件的长度printf("压缩后文件长度为:%ld个字节\n",sumlength);rate=(float)sumlength/(float)FileLength;printf("压缩率(百分比)为:%4.2f%%%\n",rate*100);fclose(ifp);fclose(ofp);return;}//返回书签//建立哈弗曼树中用于选择最小权值结点的函数int select(struct node *nodep,int pose){int i;int s1;long min=2147483647;//s初值为long型的最大值for(i=0;i<=pose;i++){if(nodep[i].parent!=-1)continue;if(nodep[i].weight<min){min=nodep[i].weight;s1=i;}}return s1;}//返回书签//哈弗曼编码函数void encode(struct node *nodep,int n){ //从叶子向根求每个字符的哈弗曼编码int start;int i,f,c;char codes[256];codes[n-1]='\0'; //编码结束符for(i=0;i<n;i++) //逐个字符求哈弗曼编码{start=n-1;for(c=i,f=nodep[i].parent;f!=-1;c=f,f=nodep[f].parent){start--;if(nodep[f].lchild==c)codes[start]='0';else codes[start]='1';}strcpy(nodep[i].code,&codes[start]);nodep[i].CodeLength=strlen(nodep[i].code);}}//返回书签//解压函数void uncompress() //解压文件{clock_t start, finish;double duration;FILE *ifp,*ofp;char infile[20],outfile[20];long FileLength,sumlength,filelength;int n,m;int i,j,k;char buf[256],codes[256];unsigned char c;int maxlength;float speed;printf("请输入要解压的文件名:");scanf("%s",infile);ifp=fopen(infile,"rb");if(ifp==NULL){printf("文件名输入错误,文件不存在!\n");return;}printf("请输入目标文件名:");scanf("%s",outfile);ofp=fopen(outfile,"wb");if(ofp==NULL){printf("文件名输入错误,文件不存在!\n");return;}start=clock() ;//开始计时fread(&FileLength,4,1,ifp);//从压缩文件读出FileLength、sumlengthfread(&sumlength,4,1,ifp);fseek(ifp,sumlength,SEEK_SET); //利用sumlength读出n的值fread(&n,4,1,ifp);printf("\n解码信息:源文件长度为%d个字节,字符种类n=%d\n",FileLength,n);for(i=0;i<n;i++)//读结点信息{fread(&hfmnode[i].ch,1,1,ifp);//字符fread(&c,1,1,ifp);//编码长度hfmnode[i].CodeLength=c;hfmnode[i].code[0]=0;if(hfmnode[i].CodeLength%8>0) m=hfmnode[i].CodeLength/8+1;//m为编码占的字节数else m=hfmnode[i].CodeLength/8;for(j=0;j<m;j++)//根据字节长度m读出编码{fread(&c,1,1,ifp);//此处c为01编码转换成的字符itoa(c,buf,2);//字符型编码转换成二进制型(首位为1)//如果编码不够8位,则说明缺少了8-k位0,因此应先在前面空缺位写0for(k=8;k>strlen(buf);k--){strcat(hfmnode[i].code,"0");}//再把二进制编码存进hfmnode.code中strcat(hfmnode[i].code,buf);}hfmnode[i].code[hfmnode[i].CodeLength]=0;//去掉编码中多余的0 }//找出编码长度的最大值maxlength=0;for(i=0;i<n;i++)if(hfmnode[i].CodeLength>maxlength)maxlength=hfmnode[i].CodeLength;//开始写入目标文件fseek(ifp,8,SEEK_SET); //指针指向编码区,开始解码filelength=0;codes[0]=0;buf[0]=0;while(1){while(strlen(codes)<maxlength)//codes小于编码长度的最大值时,继续读码{fread(&c,1,1,ifp);itoa(c,buf,2);//还原编码for(k=8;k>strlen(buf);k--){strcat(codes,"0");//把缺掉的0补上}strcat(codes,buf);//codes中此时存的为一串01编码}for(i=0;i<n;i++){ //在codes中查找能使其前weight位和hfmnode.code相同的i值,weight 即为codelengthif(memcmp(hfmnode[i].code,codes,(unsignedint)hfmnode[i].CodeLength)==0) break;}strcpy(codes,codes+hfmnode[i].CodeLength);//更新codes的值c=hfmnode[i].ch;fwrite(&c,1,1,ofp);filelength++;if(filelength==FileLength) break;//写入结束}finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;printf( "\n解压完成,解压用时为:%f seconds\n", duration );fseek(ifp,0,SEEK_SET);FileLength=0;while(!feof(ifp)){fread(&c,1,1,ifp);FileLength++;}FileLength--;speed=(float)FileLength/duration/1000;/*printf("此文件长度为:%ld个字节\n",FileLength);*/printf("\n解压速度为:%5.2fKB/S\n",speed);fclose(ifp);fclose(ofp);return;}2.2程序运行结果:1.对文件”测试.txt”进行压缩,压缩后存储在文件”目标.doc”中,压缩速率为:2055.00KB/S,压缩率为64.92%。
基于哈夫曼(haffuman)算法的文件压缩的实现(C语言)(转)
基于哈夫曼(haffuman)算法的⽂件压缩的实现(C语⾔)(转)本⽂⾸先简要阐述哈夫曼算法的基本思想,然后介绍了使⽤哈夫曼算法进⾏⽂件压缩和解压缩的处理步骤,最后给出了C语⾔实现的⽂件压缩和解压缩的源代码。
哈夫曼算法的主要思想是:①⾸先遍历要处理的字符串,得到每个字符的出现的次数;②将每个字符(以其出现次数为权值)分别构造为⼆叉树(注意此时的⼆叉树只有⼀个节点);③取所有⼆叉树种种字符出现次数最⼩的⼆叉树合并为⼀颗新的⼆叉树,新⼆叉树根节点的权值等于两个⼦节点的权值之和,新节点中的字符忽略;④重复过程③直到所有树被合并为同⼀棵⼆叉树⑤遍历最后得到的⼆叉树,⾃顶向下按路径编号,指向左节点的边编号0,指向右节点的边编号1,从根到叶节点的所有边上的0和1链接起来,就是叶⼦节点中字符的哈夫曼编码。
下图展⽰了哈夫曼编码的基本思想。
基于哈夫曼算法的⽂件压缩和解压缩过程分别说明如下:⼀、⽂件压缩:①统计词频:读取⽂件的每个字节,使⽤整数数组int statistic[MAX_CHARS]统计每个字符出现的次数,由于⼀个字节最多表⽰2^8-1个字符,所以MAX_CHARS=256就⾜够了。
在统计字符数的时候,对于每⼀个byte, 有statistic[(unsigned char)byte] 。
②构造哈夫曼树:根据statistic数组,基于哈夫曼树算法造哈夫曼树,由于构造的过程中每次都要取最⼩权值的字符,所以需要⽤优先队列来维护每棵树的根节点。
③⽣成编码:深度优先遍历哈弗曼树,得到每个叶⼦节点中的字符的编码并存⼊字符串数组char*dictionary[MAX_CHARS];④存储词频:新建存储压缩数据的⽂件,⾸先写⼊不同字符的个数,然后将每个字符及其对应的词频写⼊⽂件。
⑤存储压缩数据:再次读取待压缩⽂件的每个字节byte,由dictionary[(unsigned int)byte]得到对应的编码(注意每个字符编码的长度不⼀),使⽤位运算⼀次将编码中的每个位(BIT)设置到⼀个char类型的位缓冲中,可能多个编码才能填满⼀个位缓冲,每填满⼀次,将位缓冲区以单个字节的形式写⼊⽂件。
哈夫曼树压缩CPP实现
//
// calculte the frequency of character occurence in a file and put
// them to a Haffman_tree::freq_table
YLib::Haffman_tree::freq_table get_frequency_table_imp(std::istream& in)
{
typedef YLib::Haffman_tree::freq_table freq_table;
typedef YLib::byte_t byte_t;
#include <string>
#include <map>
namespace YLib {
typedef unsigned char byte_t;
typedef std::basic_string<byte_t> byte_string;
// note that in my design a Huffman_tree is simply a
};
struct Cmp_by_freq {
typedef Haffman_node* value_t;
bool operator()(const value_t& left, const value_t& right) const
bool empty() const { return code_table_.empty(); }
iterator begin() { return code_table_.begin(); }
基于哈夫曼树的文件压缩解压程序-示例文档
软件课程设计报告基于哈夫曼树的文件压缩/解压程序计算机科学学院××专业××班××号×××2009-10-20一需求分析1.课题要求(实现文件的压缩与解压并计算压缩率)A.描述压缩基本符号的选择方法B.运行时压缩原文件的规模应不小于5KC.提供恢复文件与原文件相同性对比功能2.设计目标A软件名称:基于哈夫曼编码的文件压缩实用程序系统B软件组成:Winhfm.exe dosHfm.exeC制作平台及相关调试工具:Windows2000sp4 Borland C++ Builder 6Dev-C++ 4.9.6.0 UltraEdit-32D运行环境:dos/win98/winMe/win2K/winxpE性能特点:1.软件由两个可执行文件组成,各具特点DosHfm.exe为dos系统应用程序,体积小,高效快捷,适用范围广。
WinHfm.exe 为windows应用程序,界面友好,使用方便。
2. 对单字节(256叶子)进行哈夫曼编码,压缩率良好2.使用二级缓冲压缩/解压技术,速度比一般算法高75%以上3.可压缩最大体积为4G的文件,达到Fat32文件系统极限4. 文件索引体积比常规算法小50%5.压缩过程中显示压缩进度并有相关信息提示6.WinHfm.exe可图形显示源文件的哈夫曼编码构造树二概要设计1.相关函数介绍dos版本程序下的有关函数1..void Haffman(int nodeCode,int length,int sum,vector< pair<int,int> >&hfmCode,vector<int> &lchild,vector<int> &rchild)哈夫曼树编码递归函数2.void indexSearch(int nodeCode,vector<int> &lchild,vector<int> &rchild,vector<int> &index,vector<int>&code)索引编码递归函数3.void makeIndex(int nodeCode,int &tt,vector<int> &index,int &indexNum,list<int> &code,vector<int> &lchild,vector<int> &rchild)索引解码递归函数4.void Compress() 压缩函数5.void UnCompress() 解压缩函数windows版本程序下的新增函数1.AnsiString ShowNowTime()计算当前时间(精确到毫秒,以字符串返回)。
第1关:基于哈夫曼树的数据压缩算法
第1关:基于哈夫曼树的数据压缩算法下载提示:该文档是本店铺精心编制而成的,希望大家下载后,能够帮助大家解决实际问题。
文档下载后可定制修改,请根据实际需要进行调整和使用,谢谢!本店铺为大家提供各种类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by this editor. I hope that after you download it, it can help you solve practical problems. The document can be customized and modified after downloading, please adjust and use it according to actual needs, thank you! In addition, this shop provides you with various types of practical materials, such as educational essays, diary appreciation, sentence excerpts, ancient poems, classic articles, topic composition, work summary, word parsing, copy excerpts, other materials and so on, want to know different data formats and writing methods, please pay attention!第1关:基于哈夫曼树的数据压缩算法在信息时代,数据的存储和传输已成为日常生活和商业活动中不可或缺的一部分。
基于哈夫曼编码的文件压缩(c++版)
基于哈夫曼编码的⽂件压缩(c++版)本博客由Rcchio原创我了解到很多压缩⽂件的程序是基于哈夫曼编码来实现的,所以产⽣了⾃⼰⽤哈夫曼编码写⼀个压缩软件的想法,经过查阅资料和⾃⼰的思考,我⽤c++语⾔写出了该程序,并通过这篇⽂章来记录⼀下⾃⼰写该程序学到的东西。
因为本⼈写的程序在压缩率上,还有提升的空间,所以本⽂将不定期更新,但程序整体的思路不会有较⼤的改动。
⼀、基于哈夫曼编码可实现压缩⽂件的原理分析在计算机中,数据的存储都是⼆进制的,并且以字节作为基本的存储单位,像英⽂字母在⽂本中占⼀个字节,汉字占两个字节,我们把这种每⼀个字符都通过相同的字节数来表达的编码形式称为定长编码。
哈夫曼编码是⼀种不定长编码⽅式,即每个字符的编码的长度可以相等也可以不相等。
在哈夫曼编码中,出现的频率越⾼的字符,其哈夫曼编码的长度就越短,相反,出现频率低的字符,哈夫曼编码的长度就越长,因此字符的哈夫曼编码的平均长度是最⼩的,这就是基于哈夫曼编码可实现⽂件压缩的基本原理。
⼆、程序的功能描述该程序可以⽤来压缩任何格式的⽂件⽣成压缩⽂件,也可以解压任何由本程序⽣成的压缩格式的⽂件。
三、完成该程序所需要的知识1、哈夫曼树,哈夫曼编码2、c++的⽂件操作3、c++的位操作四、压缩解压⽂件的过程详解众所周知,压缩⽂件的⽬的是使该⽂件在不需要使⽤时占⽤的存储空间减⼩。
当使⽤到该⽂件时,将压缩⽂件解压为原⽂件。
因此该程序应具有压缩和解压这两个基本功能。
1、压缩过程:(1)对原⽂件中出现的所有字符进⾏哈夫曼编码。
为了实现对各类型⽂件的压缩,我们要以⼆进制的⽅式⽽不是⽂本⽂件的⽅式打开⽂件。
⽂件中数据的存储以字节为单位。
⼀个字节有⼋位,因此该字节中0和1的排列⽅式共有2的⼋次⽅——256种,每⼀种排列⽅式都有⼀种字符与之对应,即⼀个字节最多可以表⽰256类字符,也就是说,原⽂件中字符的种类数最多只有256种。
将原⽂件以⼆进制的⽅式打开以后,统计原⽂件中字符的种类及每种字符出现的频度(次数)。
用C++实现文件压缩(1 哈弗曼编码)
今天下午想把文件压缩写一下,因为我觉得这个还是比较锻炼技术的,对数据结构的要求应该比较高,权当练习了吧。
我采用的压缩方式是Huffman编码,不过比较囧的是,我拼写拼错了,我拼的是h affman,在后面的代码也是出尽洋相。
huffman是非常经典的一种编码形式,不过现在好像用的不多了,但是这种压缩编码的好处在于数据是无损压缩的,而且非常经典。
在构造huffman树,要做的步骤如下:1 对文件的元素进行统计,其实就是个计数,我这里的元素是以字节为单位的,毕竟任何文件都可以转化为字节流的形式,处理起来方便的很。
2 然后对统计结果进行一次由小到大的排序,排序的key值就是元素出现的次数。
3 接着取出来排好序的数组的前两个元素,求和,并且再其左右子树上创建这两个元素的副本,然后再扔回首部,此时扔回数组中的,就是包含子树的节点了。
在这里有个代码优化的小技巧,能在一定程度上提高效率,减少不必要的操作。
4 此时原来已经排好序的数组可能会遭到破坏,必须重新进行排序,因为数组大体是有序的,所以使用此时效率最高的一步插入排序,时间复杂度仅为O(n),我专门写了个函数来实现这个功能,并且进行了简单的测试。
5 然后再取出首部的前两个元素,重复上面步骤3,直到数组中的元素个数为1.最终数组中仅存的元素,就是一棵二叉树的Root。
这样huffman树的构造就完成了。
在huffman树构造完成后,接着要做的就是根据huffman树获取其huffman编码,这个还是很简单的,就是使用递归,左树传入0,右树传入1,依次递归下去,遇到叶子节点就直接输出,当然了,如果需要添加到某个数据结构中,只要在递归函数中给个参数接口,或者设置一个全局的容器,都能解决,换言之,类似于输出重定向。
这样上面的步骤完成后,就能很好的获取到huffman编码了。
不过根据huffman 编码结果将字符型的0,1写入到二进制中的0,1还有一些比较关键的函数需要处理,还没想到比较好的点子,不过最基础的蛮力方法肯定是能写的,但是,效率就不好说了。
实验5 基于哈夫曼树的数据压缩算法_数据结构习题解析与实验指导_[共2页]
实验 5 基于哈夫曼树的数据压缩算法
实验 5
基于哈夫曼树的数据压缩算法
【实验目的】 1.掌握哈夫曼树的构造算法。 2.掌握哈夫曼编码的构造算法。 【实验内容】 问题描述 输入一串字符串,根据给定的字符串中字符出现的频率建立相应的哈夫曼树,构造哈夫曼编 码表,在此基础上可以对压缩文件进行压缩(即编码),同时可以对压缩后的二进制编码文件进行 解压(即译码)。 输入要求 多组数据,每组数据 1 行,为一个字符串(只考虑 26 个小写字母即可)。当输入字符串为“0” 时,输入结束。 输出要求 每组数据输出 2n+3 行(n 为输入串中字符类别的个数)。第 1 行为统计出来的字符出现频率 (只输出存在的字符,格式为:字符:频度),每两组字符之间用一个空格分隔,字符按照 ASCII 码 从小到大的顺序排列。第 2 行至第 2n 行为哈夫曼树的存储结构的终态(如主教材 139 页表 5.2(b), 一行当中的数据用空格分隔)。第 2n+1 行为每个字符的哈夫曼编码(只输出存在的字符,格式为: 字符:编码),每两组字符之间用一个空格分隔,字符按照 ASCII 码从小到大的顺序排列。第 2n+2 行为编码后的字符串,第 2n+3 行为解码后的字符串(与输入的字符串相同)。 输入样例 aaaaaaabbbbbccdddd aabccc 0 输出样例 a:7 b:5 c:2 d:4 17700 25600 32500 44500 56634 6 11 7 2 5
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
char bits[256]; /*定义存储哈夫曼编码的数组*/
}
header[512],tmp;
tmp 用于交换值
header[512]用于储存数据
filename[255]用于储存文件地址
[算法描述]
本程序是基于哈弗曼编码的程序
if(header[i].count!=0) header[i].b=(unsigned char)i;
4据频率(权值)大小,对结点进行排序
5构建哈曼树,亚此选择权值最小的入树
6计算权值大小
7从文件开始将字符编码每8各编入一个字节,剩下超过4位再编入下一个,
少于4位,则放入新字节
uncompress()函数
7在单字节内对相应位置补0
8从压缩文件中的按位存储还原到按字节存储字符;字符位置不改变
程序调试情况
1主界面
2解压测试
原文件
压缩
压缩得到的文件
解压测试
解压后的文件
错误测试
文件不存在
参考书籍:
《数据结构》李春葆著清华大学出版社
程序源代码/question/7777030.html?si=3
主要分为两个函数: 压缩函数void compress()
解压函数void uncompress()
主要流程如下
[算法描述]
maiHale Waihona Puke 函数compress()函数
压缩算法
1打开文件
2逐个读取文件的ASCII码,储存在c,统计频率
fread(&c,1,1,ifp);
header[c].count++;
3每个哈夫曼码值及其对应的ASCII码存放在一维数组header[i]中
解压算法
1读取原文件长度,对文件进行定位
fread(&flength,sizeof(long),1,ifp);
2取原文件字符的权值
p=(long)c;
3将f转换为二进制表示的字符串
itoa(f,buf,2);
4据哈夫曼编码的长短,对结点进行排序
5根据哈夫曼编码的长短,对结点进行排序
6通过哈夫曼编码的长短,依次解码,从原来的位存储还原到字节存储
2009级《高级语言程序设计》大作业上机报告
题目:基于哈弗曼算法的压缩
参与人员:
【姓名】 【学号】
[问题定义] 压缩机解压的实现
[开发工具]DEV-C++
[数据结构]
struct head
{
unsigned char b; /*记录字符在数组中的位置*/
long count; /*字符出现频率(权值)*/