哈夫曼编码压缩文件

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

unsigned char saveChar = 0; //用来保存二进制文件,因为char类型是1个字节,所以每8位储存一次 ,而且用unsigned无符号型,避免符号位干扰
typedef struct
{
    int value;
    int p,l,r;
}HTNode,*HuffTree;         //哈夫曼树

struct fact                   //因为不是每一篇文章中所有字符都会出现
{                            //所以结构体数组存储数组下标真正对应的字符ch以及权值weight
    char ch;//字符
    int weight;//权重
};

typedef char * * HuffCode;   //  字符指针数组用于存储各个字符对应的编码
typedef char * CHAR;

void select(HuffTree &HT,int n,int &s1,int &s2);                         //查找HT中未被使用的权值最小的两个点的下标
void CREATEHUFFTREE(HuffTree &HT,fact *ww,int n,HuffCode &HC);                //建树函数,附带完成每一个字符对应的编码
void BecomeCode(HuffCode &HC,int n,CHAR &Code,char *Text,int *match);  //由已知的各个字符的编码完成全文的编码

void Code_ToBe_Artical(CHAR &Code,HuffTree &HT,fact *Fact,int n);     //由全文的编码,用已经建立的哈弗曼树完成解码
int main()
{
    HuffTree HT=NULL;
    HuffCode  HC; //字符指针数组
    printf("请输入需要编码的文章\n");
    char Text[20000];
    CHAR Code = NULL;               //指针用于存储文章最终的编码
    gets(Text);  //写入编码
    int len=strlen(Text);//求出长度
    //写入文件
    FILE *fp=fopen("E:\\demo.txt", "w");
    //printf("15555555");
    if(fputs(Text, fp)!=EOF)
    {
        printf("写入E:\\demo.txt文件成功,大小为 %dk",len/1024+1);
        //printf("%d",len);
    }
    fclose(fp);
    int codeweight[54];//频率数组
    int match[54];
    memset(codeweight,0,sizeof(codeweight));//置0
    for(int i=0;i<len;i++)          // 统计频率  空格下标为0 ,(A~Z)下标分别为(1~26) (a~z)下标分别为(27~52)
    {
        if(Text[i]==' ')
            codeweight[0]++;
        else if(isupper(Text[i]))//若是大写字母
            codeweight[Text[i]-'A'+1]++;
        else
            codeweight[Text[i]-'a'+27]++;
    }

    int n=0;
    fact Fact[54];                  // 由于不是每一个字符都出现在文章中,将codeweight数组录入Fact结构体数组中
    for(int i=0;i<=52;i++)
    {
        if(codeweight[i]!=0)
        {
            if(i==0)
                Fact[n].ch=' ';
            else if(i<=26)  //转为相应的大写字母
                Fact[n].ch=i+'A'-1;
            else
                Fact[n].ch=i+'a'-27;//转为相应的小写字母
            match[i]=n;
            Fact[n++].weight=codeweight[i];
        }
    }
    CREATEHUFFTREE(HT,Fact,n,HC);                //建树函数,附带完成每一个字符对应的编码
    BecomeCode(HC,n,Code,Text,match);     //由已知的各个字符的编码完成全文的编码
    Code_ToBe_Artical(Code,HT,Fact,n);   //由全文的编码,用已经建立的哈弗曼树完成解码
    return 0;
}

void select(HuffTree &HT,int n,int &s1,int &s2)  //查找HT中未被使用的权值最小的两个点的下标
{
    s1=s2=0;
    HT[0].value=0x7fffffff;
    for(int i=1;i<=n;i++)
    {
        if(HT[i].p!=0)
            continue;
        if(HT[i].value<HT[s1].value)//比较权值
        {
            s2=s1;
            s1=i;
        }
        else if(HT[i].value<HT[s2].value)
            s2=i;
    }
}

void CREATEHUFFTREE(HuffTree &HT,fact *ww,int n,HuffCode &HC) //由已知的各个字符的编码完成全文的编码
{
    int m=n*2-1;
    HT = (HuffTree)malloc((m+1)*sizeof(HTNode));  //分配m+1个内存,是因为要存储m个数据,但是要从HT数组下标1开始
    int i,j,f;
    fact *w=ww;
    HuffTree p;
    for(p =HT,p++,i=1;i<=n;i++,p++,w++)          //对HT (1~n)赋值语句
    {
        (*p).value=(*w).weight,(*p).p=0,(*p).l=0,(*p).r=0;
    }
    for(;i<=m;i++,p++)                          //对HT (n+1~m)赋值语句
    {
        (*p).value=0,(*p).p=0,(*p).l=0,(*p).r=0;
    }

    int s1,s2;
    for(i=n+1;i<=m;i++)
    {
        select(HT,i-1,s1,s2);//查找最小的两个
        HT[s1].p=i,HT[s2].p=i;
        HT[i].l=s1,HT[i].r=s2;
        HT[i].value=HT[s1].value+HT[s2].value;
    }

    HC = (HuffCode)malloc((n+1)*sizeof(char *));    // 为字符指针数组分配内存
    char *temp=(char *)malloc(n*sizeof(char));
    temp[n-1]='\0';
    for(i=1;i<=n;i++)
    {
        int start=n-2;
        for(j=i,f=HT[i].p;f!=0;j=f,f=HT[f].p)
        {
            if(HT[f].l==j)
                temp[start--]='0';
            else
                temp[start--]='1';
        }
        HC[i]=(char *)malloc((n-start)*sizeof(char));
        strcpy(HC[i],&temp[++start]);
    }
    delete temp;

    printf("\n各个字符对应的编码\n");
    for(i=1;i<=n;i++)
    {
        if(ww[i-1].ch==' ')
            printf("空格 --> ");
        else
            printf("%c --> ",ww[i-1].ch);
        puts(HC[i]);
    }
}

void BecomeCode(HuffCode &HC,int n,CHAR &Code,char *Text,int *match)  //由已知的各个字符的编码完成全文的编码
{
    int len,i;                           //纯粹是用已知的文本Text和HC将文本转化为编码
    len=strlen(Text);
    Code = (char *)malloc((len*n+1)*sizeof(char));//初始化字符数组
    Code[0]='\0';  //置空
    for(i=0;i<len;i++)
    {
        if(Text[i]==' ')
            strcat(Code,HC[1]);
        else if(Text[i]<='Z'&&Text[i]>='A')
            strcat(Code,HC[ match[Text[i]-'A'+1]+1 ]);
        else
            strcat(Code,HC[ match[Text[i]-'a'+27]+1 ]);
    }
    printf("\n文章编码为\n");
    puts(Code);
    FILE *fp=fopen("E:\\test.txt", "w");
    if(fputs(Code, fp)!=EOF)
    {
        printf("文章编码写入E:\\test.txt文件成功\n");
    }
    fclose(fp);
    //进行压缩
    FILE *fp2=fopen("E:\\test.txt", "r");
    FILE *fpw = fopen("E:\\Huffman","wb");//2进制写入文件
    char reder;
    int Hufflength=0;//压缩长度
    int num=0;//计数
    //int  t=0;
    //printf("len=%d\n",strlen(Code));
   // while ((reder=fgetc(fp2))!=EOF)//一个一个读入字符
    {
        //t++;

        for(int i=0;i<strlen(Code);i++)
        {
            //saveChar || = (code[i]-'0');
            //printf("%c",Code[i]);
            saveChar  = ((Code[i]-'0')|saveChar);//让saveChar和编码中的每一位进行或操作
            num++;
            if(num==8)
            {
                fwrite(&saveChar,sizeof(char),1,fpw);//每8位写入一次文件
                Hufflength++;
                saveChar = 0;//重新置0
                num = 0;
            }
            else{
                    saveChar = saveChar << 1; //每做完一步,左移一位

            }
        }
    }
    //printf("t=%d",t);
    //最后不到8位,移到最左端
    if(num != 8)
    {
        saveChar = saveChar<<(7-num);//移到最左端
        fwrite(&saveChar,sizeof(char),1,fpw);
        Hufflength++;
    }
    printf("加密文件写入E:\\Huffman 成功,大小为 %dk",Hufflength/1024+1);
    //printf("%d",Hufflength);
    fclose(fp2);
    fclose(fpw);

}

void  Code_ToBe_Artical(CHAR &Code,HuffTree &HT,fact *Fact,int n)  //根据哈夫曼编码解压缩,主要思想是根据编码遍历哈夫曼树
{

    printf("\n将编码解码\n");
    for(int i=0;Code[i]!='\0';i++)//遍历哈夫曼编码
    {
        int m=n*2-1,ok=1;
        while(1)
        {
            if(Code[i]=='0')
            {
                m=HT[m].l;
                if(HT[m].l==0)
                {
                    printf("%c",Fact[m-1].ch);
                    break;
                }
            }
            else if(Code[i]=='1')
            {
                m=HT[m].r;
                if(HT[m].r==0)
                {
                    printf("%c",Fact[m-1].ch);
                    ok=0;
                }
            }
            if(!ok)
                break;
            i++;
        }
    }
    printf("\n");
    return ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Corleone_1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值