赫夫曼编码解码C语言实现

赫夫曼编码解码


一、实验目的
掌握赫夫曼树和赫夫曼编码的基本思想和算法的程序实现。

二、 实验内容及要求
任务描述
实现文件中数据的加解密与压缩:将硬盘上的一个文本文件进行加密,比较加密文件和原始文件 的大小差别;对加密文件进行解密,比较原始文件和解码文件的内容是否一致。

程序源代码:

DS.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

HFTree.h

#include <DS.h>
typedef struct HTNode
{
    int value;
    int p,l,r;
} HTNode, *HuffTree;

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

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

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

HFTree.c

#include <HFTree.h>
void select(HuffTree HT,int n,int *s3,int *s4)
{
    int s1 = *s3,s2 = *s4;
    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;
    }
    *s3 = s1;
    *s4 = s2;
}

void HUFFTREE(HuffTree *HT1,fact *ww,int n,HuffCode *HC1)
{
    HuffTree HT;
    HT = *HT1;
    HuffCode HC;
    HC = *HC1;
    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=0,s2=0;
    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]);
    }
    free(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]);
    }

    *HC1 = HC;
    *HT1 = HT;
}

void BecomeCode(HuffCode *HC1,int n,CHAR *Code1,char *Text,int *match)
{
    HuffCode HC;
    HC = *HC1;
    CHAR Code;
    Code = *Code1;
    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);
    *HC1 = HC;
    *Code1 = Code;

    FILE *fp;
    if((fp=fopen("e:\\1.bin","w"))==NULL)
    {
        printf("打开文件失败,写操作失败");
    }
    fputs(Code,fp);
    fclose(fp);
    printf("加密后的文件是1.bin\n");
    printf("加密后文件的路径是e:\\1.bin\n");
}

void Code_ToBe_Artical(CHAR Code,HuffTree HT,fact *Fact,int n) //纯粹的使用已经建好的树,由上至下进行查询
{
    FILE *p;
    char chh;
    if((p=fopen("e:\\decode.txt","a+"))==NULL){
        printf("ERROR");
    }

    printf("\n解码后的文件地址为e:\\decode.txt");
    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);
                    chh=Fact[m-1].ch;
                     fputc(chh,p);
                    break;
                }
            }
            else if(Code[i]=='1')
            {
                m=HT[m].r;
                if(HT[m].r==0)
                {
                    printf("%c",Fact[m-1].ch);
                    chh=Fact[m-1].ch;
                    fputc(chh,p);
                    ok=0;
                }
            }
            if(!ok)
                break;
            i++;
        }
    }
    printf("\n");
    fclose(p);
    return ;
}

main.c

#include <HFTree.h>
int main()
{
    FILE *fp;
    char ch;
    int a=0;
    HuffTree HT=NULL;
    HuffCode HC;
    printf("请输入需要编码的文件地址:\n");
    char Text[20000];
    char location[100];
    gets(location);
    CHAR Code = NULL; //指针用于存储文章最终的编码
    if((fp = fopen(location,"r")) == NULL)
    {
        printf("File open error!\n");
        exit(0);
    }
    for( ; (ch = fgetc(fp)) != EOF;)
    {
        Text[a]=ch;
        a++;
        putchar(ch);
    }
    fclose(fp);
    //gets(Text);
    int len=strlen(Text);
    int codeweight[54],match[54];
    memset(codeweight,0,sizeof(codeweight));
    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];
        }
    }
    HUFFTREE(&HT,Fact,n,&HC); //建树函数,附带完成每一个字符对应的编码
    BecomeCode(&HC,n,&Code,Text,match);
    Code_ToBe_Artical(Code,HT,Fact,n); //由全文的编码,用已经建立的哈弗曼树完成解码
    return 0;
}

测试:
在这里插入图片描述
求个赞。。。

  • 16
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小小本科生debug

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

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

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

打赏作者

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

抵扣说明:

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

余额充值