限制只能操作1Mb以内的文件
#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[n]!='.'&&cfile[n])++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[n]!='.'&&cfile[n])++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;
}