哈夫曼编码译码器,压缩比(详细运行过程)

本人小辣鸡一枚,学校的一个数据结构作业。在此记录。
编译器:codeblocks
功能:
1.选择文件建立哈夫曼树
2.建立密码本,对文件进行编码
3.选择需要进行解码的文件解码
4.按位压缩方式压缩与解压,并且显示压缩比
运行操作及效果

目录结构:
目录结构

11.txt 必须有,剩下的随意,可以最初没有。让11.txt和自己的cpp文件放到一个文件夹里面。11.txt 里面随便写点什么,我写的内容是:
bbbbbbb
AAAAAA
cc
ddddd
eeeeeeeee

下面是我的运行过程,运行完之后,22.txt存的是编码文件,33.txt存的是编码文件解码后的文件,经过验证,和11.txt内容应该相同。44.txt是22.txt按位压缩后的结果,55.txt是44.txt解码后的结果,应该和22.txt内容相同。最后得到的压缩比应该大概是1/8
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<windows.h>
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<fstream>
using namespace std;
typedef struct HTNode{
    char data;
    int weight;
    int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode;
char myarr[1000]={NULL};
int mcnt;
int pData;//原文件
int nData;//压缩后的的文件
int multiple;
/*******************************************选择文件**************************************/
void ChooseFile(){
    char n[20];
    FILE *fp;
    char ch;
    int i=1;
    cout<<"输入要打开的文件"<<endl;
    cin>>n;
    fp=fopen(n,"r");
    if(fp==NULL)
        printf("打开失败!\n");
    else{
        fscanf(fp,"%c",&ch);
        while(!feof(fp)){
            myarr[i]=ch;
            fscanf(fp,"%c",&ch);
            i++;
        }
        fclose(fp);
        myarr[i]='\0';
    }
    pData=i-1;
    cout<<"打开原始文件"<<n<<"成功!";
    cout<<endl;
}
/*******************************************求文件中叶子结点的权值**************************************/
void Count(HuffmanTree &HT){//统计
    mcnt=0;
    int cnt[256]={0};
    for (int i=1;myarr[i]!='\0';i++)
        cnt[myarr[i]]++;
    for (int i=0;i<256;i++){
        if(cnt[i]>0){
            mcnt++;
        }
    }
    int m=2*mcnt-1;
    HT=new HTNode[m+1];
    int mcnt2=0;
    for (int i=0;i<256;i++){
        if(cnt[i]>0){
            mcnt2++;
            //cout<<"字符"<<(char)i<<"出现了"<<cnt[i]<<"次"<<endl;
            HT[mcnt2].weight=cnt[i];
            HT[mcnt2].data=(char)i;
        }
    }
}
/*****************************************哈夫曼编码******************************************************/
void HuffmanCODE(HuffmanTree HT, HuffmanCode &HC, int n){
    HC=new char *[n+1];
    char * cd=new char[n];
    cd[n-1]='\0';
    int start,c,f;
    for(int i=1;i<=n;i++)
    {
        start=n-1;
        c=i;
        f=HT[i].parent;
        while(f!=0)
        {
            start--;
            if(HT[f].lchild==c){
                cd[start]='0';
            }
            else{
                cd[start]='1';
            }
            c=f;
            f=HT[f].parent;
        }
        HC[i]= new char[n-start];
        strcpy(HC[i],&cd[start]);
    }
    for(int i=1;i<=mcnt;i++){
        cout<<HT[i].data<<"出现频度是:"<<HT[i].weight<<" 编码为"<<HC[i]<<endl;
    }
    delete cd;
    ofstream file;
    char fl[100];
    cout<<"输入要写入的文件"<<endl;
    cin>>fl;
    file.open(fl,ios::out);
    for(int i=1;myarr[i]!='\0';i++){
        for(int j=1;j<=mcnt;j++){
            if(myarr[i]==HT[j].data){
                    //hc[j]字符串数组
                file <<HC[j];
                break;
            }
        }
    }
    file.close();
}
/*****************************************哈夫曼译码******************************************************/
void HuffmanDECODE(HuffmanTree HT,int n){
    char decode[1000];
    FILE *fp;  //创建一个文件指针*fp
    char ch;
    int mcnt3=0;
    char f1[20];
    cout<<endl<<"输入要解码哪个文件"<<endl;
    cin>>f1;
    fp=fopen(f1,"r");
    if(fp==NULL)
        printf("打开失败!\n");
    else{
        int i=0;
        fscanf(fp,"%c",&ch);
        while(!feof(fp)){
            decode[i]=ch;
           // putchar(ch);
            fscanf(fp,"%c",&ch);
            i++;
            mcnt3++;
        }
        fclose(fp);
        decode[i]='\0';
    }
    cout<<endl;
    ofstream file;
    char fl[100];
    cout<<"输入要写入的文件"<<endl;
    cin>>fl;
    file.open(fl, ios::out);
    int i,p;
    p=2*n-1;
    for(i=0;i<=mcnt3;i++){
        if(HT[p].lchild==0&&HT[p].rchild==0){
           // printf("%c",HT[p].data);
            file <<HT[p].data;
            p=2*n-1;
        }
        if(decode[i]=='0')
            p=HT[p].lchild;
        else if(decode[i]=='1')
            p=HT[p].rchild;
    }
    file.close();
    printf("译码成功\n");
}
/********************************找到父节点不为0的两个叶子结点**********************************/
void Select(HuffmanTree &HT,int i,int &s1,int &s2){
    s1=1;
    s2=1;
    int sm1=9999;
    int sm2=9999;
    for(int j=1;j<i;j++){
        if(HT[j].parent==0){
            if(HT[j].weight<sm1){
                sm2=sm1;
                sm1=HT[j].weight;
                s2=s1;
                s1=j;
            }
            else if(HT[j].weight<sm2){
                sm2=HT[j].weight;
                s2=j;
            }
        }
    }
}
/****************************************创建哈夫曼树**************************************/
void CreatHuffmanTree(HuffmanTree &HT){

    int m=2*mcnt-1;
    for(int i=1;i<=m;i++){
        HT[i].parent=0;
        HT[i].lchild=0;
        HT[i].rchild=0;
        if(i>mcnt){
            HT[i].weight=0;
        }
    }
    for(int i=mcnt+1;i<=m;i++){
        int s1=1,s2=1;
        Select(HT,i,s1,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;
    }
    cout<<"创建哈夫曼树成功!"<<endl;
    cout<<"字符   "<<"权值   "<<"双亲   "<<"左孩子   "<<"右孩子   "<<endl;
    for(int i=1;i<=m;i++){
        if(i<=mcnt){
            cout<<HT[i].data<<"        ";
        }
        else{
            cout<<"           ";
        }
        cout<<HT[i].weight<<"      ";
        cout<<HT[i].parent<<"      ";
        cout<<HT[i].lchild<<"      ";
        cout<<HT[i].rchild<<"        ";
        cout<<endl;
    }
}
/******************************二进制转十进制************************/
int binary2decimal(char str[]){
    int sum=0;
    int j=1;
    int pos = strlen(str) - 1;
    for(;pos>=0;pos--)
    {
        sum+=(str[pos]-'0')*j;
        j*=2;
    }
    return sum;
}
/*********************************压缩*********************************/
void Compress(){
    int mcount;
    char decode[1000];
    FILE *fp;  //创建一个文件指针*fp
    char ch;
    char f1[20];
    int mcnt3=0;//存储有多少个1 0
    cout<<endl<<"输入要打开哪个编码文件进行压缩?"<<endl;
    cin>>f1;
    fp=fopen(f1,"r");
    cout<<endl;
    if(fp==NULL)
        printf("打开失败!\n");
    else{
       // cout<<"编码文件的内容是:"<<endl;
        int i=0;
        fscanf(fp,"%c",&ch);
            while(!feof(fp)){
                decode[i]=ch;
              //  putchar(ch);
                fscanf(fp,"%c",&ch);
                i++;
                mcnt3++;
            }
        fclose(fp);
        decode[i]='\0';
    }
    cout<<endl;
    //已经实现把所有二进制都读入到decode数组里了,下标从0开始

    //补齐0
    int k;
    if(mcnt3%8!=0){
        multiple=8-mcnt3%8;
        for(k=0;k<multiple;k++){
            decode[mcnt3+k]='0';
        }
        decode[mcnt3+k]='\0';
    }

    char data[10];
    data[8]='\0';
    ofstream file;
    char fl[100];//解压后.txt
    cout<<"输入把编码文件压缩到哪个文件"<<endl;
    cin>>fl;
    file.open(fl,ios::out|ios::binary);

    for(mcount=0;mcount<mcnt3+k;mcount++){
        data[mcount%8]=decode[mcount];//8个存到data数组
        if((mcount+1)%8==0){
            int result=binary2decimal(data);//变成十进制
            char res=(char)result;
            file<<res;
        }
    }
    file.close();

}
/*****************************十进制转二进制**************************/
int decimal2binary(int x,int &flag,int &digit){
    int p=1,y=0,yushu;
    int cont=0;
    while(1)
    {
        yushu=x%2;
        x/=2;
        y+=yushu*p;
        p*=10;
        if(x<2)
        {
            y+=x*p;
            break;
        }
    }
    int temp=y;
    while(temp!=0)
    {
        temp/=10;
        cont++;
    }
    if(cont!=8){
        flag=1;
        digit=8-cont;
    }
    return y;
}
/*******************************解压*****************************/
void Decompression(){
    int decode[1000];
    FILE *fp;  //创建一个文件指针*fp
    char ch;
    char f1[20];
    int mcnt3=0;//存储有多少个1 0
    cout<<endl<<"输入要解压哪个文件?"<<endl;
    cin>>f1;
    fp=fopen(f1,"r");
    cout<<endl;
    if(fp==NULL)
        printf("打开失败!\n");
    else{
        int i=0;
        fscanf(fp,"%c",&ch);
        while(!feof(fp)){
            decode[i]=ch;
            int data =ch;
            if(data<0){
                data=256+data;
                decode[i]=data;
                //mcnt3++;
            }
            fscanf(fp,"%c",&ch);
            i++;
            mcnt3++;
        }
        decode[i]='\0';
        nData=i;
        fclose(fp);
    }
    ofstream file;
    char fl[100];//解压后.txt
    cout<<endl<<"输入解压后的编码文件存在哪里?"<<endl;
    cin>>fl;
    file.open(fl,ios::out|ios::binary);
    char temp[1000];
    for(int i=0;decode[i]!='\0';i++){
        int flag=0;
        int dight=0;
        int res=decimal2binary(decode[i],flag,dight);
        if(flag==1){
            for(int y=1;y<=dight;y++){
                char c='0';
                file<<c;
            }
        }
        file<<res;
    }
    file.close();

    FILE *fp2;
    fp2=fopen(fl,"r");
    cout<<endl;
    if(fp2==NULL)
        printf("打开失败!\n");
    else{
        int i=0;
        fscanf(fp2,"%c",&ch);
        while(!feof(fp2)){
            if(i<(mcnt3)*8-multiple){
                temp[i]=ch;
                fscanf(fp2,"%c",&ch);
                i++;
            }
            else{
                break;
            }
        }
        fclose(fp2);
        temp[i]='\0';
    }
    ofstream file2;
    file2.open(fl,ios::out|ios::binary);
    for(int i=0;temp[i]!='\0';i++){
            file2<<temp[i];
    }
    file2.close();
}
void setColor(unsigned short ForeColor=4,unsigned short BackGroundColor=15){
    HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);//获取当前窗口句柄
    SetConsoleTextAttribute(handle,ForeColor+BackGroundColor*0x10);//设置颜色
}
/*********************************压缩比*********************************/
void CompressionRatio(){
    cout<<"输入压缩前的文件是?"<<endl;
    ChooseFile();
    cout<<endl;
    int decode[1000];
    FILE *fp;  //创建一个文件指针*fp
    char ch;
    char f1[20];
    int mcnt3=0;//存储有多少个1 0
    cout<<endl<<"输入压缩后的文件是?"<<endl;
    cin>>f1;
    fp=fopen(f1,"r");
    cout<<endl;
    if(fp==NULL)
        printf("打开失败!\n");
    else{
        int i=0;
        fscanf(fp,"%c",&ch);
            while(!feof(fp)){
                decode[i]=ch;
                int data =ch;
                if(data<0){
                    data=256+data;
                    decode[i]=data;
                    //mcnt3++;
                }
                fscanf(fp,"%c",&ch);
                i++;
                mcnt3++;
            }
        decode[i]='\0';
        nData=i;
        fclose(fp);
    }
    double raTio=1.0*nData/pData*100;
    cout<<"压缩比是:"<<nData<<"/"<<pData<<"="<<raTio<<"%"<<endl;
}
void HomePage(){
    cout<<"========================================================"<<endl;
    cout<<"*                *哈夫曼编码译码器*                    *"<<endl;
    cout<<"*             1、选择需要进行编码的文件                *"<<endl;
    cout<<"*             2、建立哈夫曼树                          *"<<endl;
    cout<<"*             3、建立密码本并对文件编码                *"<<endl;
    cout<<"*             4、选择需要进行解码的文件并解码          *"<<endl;
    cout<<"*             5、按位压缩方式对文件进行压缩            *"<<endl;
    cout<<"*             6、显示压缩比                            *"<<endl;
    cout<<"*             7、解压                                  *"<<endl;
    cout<<"*             8、退出                                  *"<<endl;
    cout<<"========================================================"<<endl;
}
main(){
    setColor();
    //system("color 3");
    HuffmanTree hf;
    HuffmanCode hc;
    while(1){
        HomePage();
        cout<<"输入您想进行的操作是(1-8):";
        int n;
        cin>>n;
        if(n==1){
            ChooseFile();//第一个功能
        }else if(n==2){
            Count(hf);
            CreatHuffmanTree(hf);//第二个功能
        }else if(n==3){
            HuffmanCODE(hf,hc,mcnt);//第三个功能
        }else if(n==4){
            HuffmanDECODE(hf,mcnt);//第四个功能
        }else if(n==5){
            Compress();//压缩
        }else if(n==6){
            CompressionRatio();
        }else if(n==7){
            Decompression();
        }else if(n==8){
            return 0;
        }else{
            cout<<"输入错误!";
        }
        system("pause");
        system("cls");
    }
}

哈夫曼树网上有很多的讲解。
压缩和解压的代码不多。。。
8位是256个(0-255)。。。然后char存字符。。存0-255的数字,刚好是8位,2的8次方。
所以把编码后的每8位都变成一个十进制的数字,存在char中,然后翻译再对照后翻译回十进制,再变成二进制。。。如果最后不足八位,补齐0,补成8位,解码时候注意去掉0。

欢迎各位大佬多多指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值