#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 ;
}
哈夫曼编码压缩文件
最新推荐文章于 2022-04-05 10:56:53 发布