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

限制只能操作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;  
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值