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

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值