您的位置:首页 > 其它

使用haffman(哈夫曼)编码的简单压缩软件

2013-12-21 15:13 309 查看
限制只能操作1Mb以内的文件

学习haffman编码时写的软件,挂出来分享下下~

同样学习haffman编码的同学可以参考~~也希望大家帮助我改正错误~~

by 小夜

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;

int getbit(int c,int w) //获取二进制数c的第w位
{
return (c>>w)&1;
}

class tree{     //用来生成haffman树,并记录haffman编码与字符的对应关系
public:
int f;      //字符频率
char ch;    //字符
int code;   //字符编码
int c_len;  //编码长度
tree *left,*right;  //左右子树
tree()      //构造
{
f=c_len=code=0;
left=right=0;
}
~tree()     //析构
{
if(left)delete left;
if(right)delete right;
}
void preorder(int c,int len)    //先序遍历,生成haffman编码
{
code=c;
c_len=len;
if(left)left->preorder(c<<1,len+1);
if(right)right->preorder((c<<1)|1,len+1);
}
};

class haffman{      //实现压缩与解压
char rfile[100];    //读入文件名
char zfile[100];    //压缩后文件名(读入文件后自动生成)
char cfile[100];    //编码文件名  (读入文件后自动生成)
tree *ba[551];      //基础字符编码树
tree *pt;           //haffman树 根结点(供删除haffman树,清理内存)
int b_len;          //基础字符树个数
char *buffer,*r;    //缓冲区,读入文件内容
int lenth;          //读入内容长度
int max_bu,bu;      //最大缓冲区段基值,偏移量
int max_bit;        //最长haffman编码倍数(没有用到)
public:
haffman()   //构造
{
pt=0;
buffer=new char[1024*1024];
r=new char[1024*1024];
memset(ba,0,sizeof(ba));
}
~haffman()  //析构
{
delete buffer;
delete r;
delete pt;
}
void read();                //读入文件内容到 r
void calfreq();            //计算字符出现频率,保存在ba基础字符树中
void qs(tree *a[],int,int);//对字符树按频率进行排序
void makeh();           //生成haffman树,并获得基础字符的haffman编码
void wbit(int i);       //向缓冲区写入一个字节
void zip();             //压缩
void write();           //写入文件缓冲区中的内容
void savecode();        //保存haffman编码和缓冲区大小,供解压用
void readbuffer();      //读入压缩文件内容到缓冲区
void readcode();        //读取haffman编码和缓冲区大小,供解压用
void unzip();           //解压并保存文件
void show();            //显示基础字符的haffman编码 调试用
void showbuffer();      //输出压缩后缓冲区内容的二进制编码 调试用
};
void haffman::read()        //读入文件内容到 r
{
FILE *pf;
int n=0;
cout<<"打开文件: ";cin>>rfile;
pf=fopen(rfile,"rb");
if(pf==0)
{
cout<<"文件打开失败!"<<endl;
return;
}
strcpy(cfile,rfile);
while(cfile
!='.'&&cfile
)++n;
strcpy(cfile+n,".cod"); //自动生成保存编码的文件名

char *s=r;
n=0;
lenth=0;
do
{
n=fread(s,sizeof(char),1024,pf);
lenth+=n;
if(lenth>1024*1024)
{
lenth=1024*1024;
break;
}
s+=n;
}while(n==1024);
fclose(pf);
}
void haffman::qs(tree *a[],int b,int e) //排序,供生成haffman树
{
if(b>=e)return ;
tree *t;
int k=a[b]->f,l=b,r=e;
while(l<r)
{
while(l<r&&a[r]->f>=k)r--;
t=a[l];a[l]=a[r];a[r]=t;
while(l<r&&a[l]->f<k)l++;
t=a[l];a[l]=a[r];a[r]=t;
}
a[r]=a[l];
qs(a,b,r-1);
qs(a,r+1,e);
}
void haffman::calfreq()         //计算频率,供生成haffman树
{
b_len=0;
char *s=r;
for(int i=0;i<lenth;++i)   //计算频率
{
int j,ok=1;
for(j=0;ba[j];++j)
{
if(ba[j]->ch==*s)
{
ba[j]->f++;
ok=0;
break;
}
}
if(ok)
{
ba[j]=new tree;
ba[j]->f=1;
ba[j]->ch=*s;
++b_len;
}
++s;
}
}
void haffman::makeh()   //生成haffman树并先序遍历获得haffman编码
{
tree *tr[b_len];
memcpy(tr,ba,sizeof(tree*)*b_len);
int i=0;
if(pt)delete pt;
while(i<b_len-1)    //生成树
{
qs(tr,i,b_len-1);
pt=new tree;
pt->f=tr[i]->f+tr[i+1]->f;
pt->left=tr[i];
pt->right=tr[i+1];
tr[i+1]=pt;
++i;
}
pt->preorder(0,0);//获得haffman编码
}
void haffman::wbit(int i)   //向缓冲区写入一个字节
{
buffer[max_bu]=buffer[max_bu]|(i<<bu);
++bu;
if(bu>=8)   //自动增加缓冲区大小
{
bu=0;
++max_bu;
}
}
void haffman::zip()     //压缩字符串到缓冲区
{
max_bu=bu=0;
char *s=r;
for(int i=0;i<lenth;++i)    //压缩内容到buffer,直到 r 内容结束
{
int j;
for(j=0;j<b_len;++j)
{
if(ba[j]->ch==*s)break;
}
for(int k=ba[j]->c_len-1;k>=0;--k)
{
wbit(1&(ba[j]->code>>k));
}
++s;
}
}
void haffman::write()   //缓冲区写入文件
{
FILE *pf;
char s[100];
cout<<"保存为: ";cin>>s;
int n=0;
strcpy(cfile,s);
while(cfile
!='.'&&cfile
)++n;
strcpy(cfile+n,".cod"); //自动生成保存编码的文件名

pf=fopen(s,"wb");
if(pf==0)
{
cout<<"文件打开失败!"<<endl;
return;
}
for(int i=0;i<=max_bu;++i)
{
fputc(buffer[i],pf);
}
fclose(pf);
}
void haffman::savecode()
{
FILE *pf;
pf=fopen(cfile,"w");
if(pf==0)
{
cout<<"文件打开失败!"<<endl;
return;
}
fprintf(pf,"%d %d\n",max_bu,bu);//保存缓冲区大小
for(int i=0;i<b_len;++i)//保存字符 编码 编码长度
{
fprintf(pf,"%d %d %d\n",ba[i]->ch,ba[i]->code,ba[i]->c_len);
}
fclose(pf);
}
void haffman::readbuffer()      //读取文件内容到缓冲区
{
read();
memcpy(buffer,r,lenth*sizeof(char));
}
void haffman::readcode()
{
FILE *pf;
pf=fopen(cfile,"r");
if(pf==0)
{
cout<<"文件打开失败!"<<endl;
return;
}
b_len=0;
fscanf(pf,"%d %d",&max_bu,&bu); //读取缓冲区大小
while(!feof(pf))
{
ba[b_len]=new tree; //读取字符 编码 编码长度
if(fscanf(pf,"%d %d %d",&ba[b_len]->ch,&ba[b_len]->code,&ba[b_len]->c_len)==3)++b_len;
}
fclose(pf);
}
void haffman::unzip()
{
int seg=0,i=0;  //记录目前读取位置
int c=0,len=0;
char ufile[100];
//   show();
//   showbuffer();
FILE *pf;
cout<<"解压为: ";cin>>ufile;
pf=fopen(ufile,"wb");
if(pf==0)
{
cout<<"文件打开失败!"<<endl;
return;
}
while(seg<max_bu||(seg==max_bu&&i<bu))//解压,直到缓冲区结束
{
c=c<<1;
c|=1&(buffer[seg]>>i);
++i;
if(i>=8)
{
i=0;
++seg;
}
++len;
for(int j=0;j<b_len;++j)
{
if(len==ba[j]->c_len&&c==ba[j]->code)
{
len=c=0;
//       putchar(ba[j]->ch);
fputc(ba[j]->ch,pf);
break;
}
}
}
fclose(pf);
}
void haffman::show()    //显示字符对应haffman编码 调试用
{
for(int i=0;i<b_len;++i)    //按 字符:编码长度:编码 显示字符对应haffman编码
{
int len=ba[i]->c_len;
cout<<ba[i]->ch<<": "<<len<<": ";
for(int j=len-1;j>=0;--j)
{
cout<<(1&(ba[i]->code>>j));
}cout<<endl;
}
}
void haffman::showbuffer()  //显示缓冲区中的二进制编码 调试用
{
int seg=0,i=0;
while(seg<max_bu||(seg==max_bu&&i<bu))  //判读是否到缓冲末尾
{
cout<<(1&(buffer[seg]>>i));
++i;
if(i>=8)
{
i=0;
++seg;
cout<<" ";
}
}
}
int menu()      //菜单
{
haffman a;
int n;
cout<<endl;
cout<<"1.压缩"<<endl;
cout<<"2.解压"<<endl;
cout<<"3.退出"<<endl;
cout<<"请选择: ";
cin>>n;
switch(n)
{
case 1:     //压缩过程
a.read();       //读入文件
a.calfreq();    //获取字符出现频数
a.makeh();      //生成字符haffman编码
a.zip();        //压缩字符,存入缓冲区
a.write();      //将缓冲区内容写入文件
a.savecode();   //保存haffman编码和缓冲区大小 ,供解压时使用
break;
case 2:
a.readbuffer(); //将文件内容读入缓冲区
a.readcode();   //读入保存的编码和缓冲区大小
a.unzip();      //解压并保存文件
break;
default:
return 0;
}
return 1;
}
int main()
{
while(menu());
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: