哈夫曼编码实现压缩解压缩

         这也是我们做的作业,哈夫曼编码可用于压缩解压缩,平时我们用的rar也差不多是用这个原理来压缩的。代码主体也是从网上拷的,而且这个程序说实话,并没有付入很多心血,注释也很少,给大家看看吧:

 

#include <iostream>
#include <conio.h>
using namespace std;

typedef struct huffNode
{
 int parent,lchild,rchild;
 unsigned long count;
 unsigned char c;
 char bits[256];

}HuffNode;
HuffNode HTree[512],temp;

void compress();
void uncompress();

void main()
{
 char a;
 while(1)
 {
  cout<<"                         压缩、解压缩工具                 /n/n"; 
  cout<<"功能:/n";
  cout<<"     1.压缩/n";
  cout<<"     2.解压缩/n";
  cout<<"     3.退出/n/n";
  cout<<"注意:使用本软件压缩后压缩文件后缀名为ty/n";
  cout<<"      压缩和解压时不需输入压缩文件的后缀名/n/n";
  cout<<"请选择操作:";
  cin>>a;
  while(a!='1' && a!='2' && a!='3')
  {
   cout<<"无效的输入!/n";
   cout<<"请选择操作:";
   cin>>a;
  }
  if(a=='1')
   compress();
  else if(a=='2')
   uncompress();
  else
   exit(0);
  system("cls");
 }
}

/************压缩************/
void compress()
{
 FILE *infile,*outfile;
 char infilename[255],outfilename[255];
 cout<<"/n请输入要压缩的文件名:";
 cin>>infilename;
 fopen_s(&infile,infilename,"rb");
 while(infile==NULL)
 {
  char a;
  cout<<"文件"<<infilename<<"不存在.../n";
  cout<<"重新输入文件名(1)或返回主菜单(2)?";
  cin>>a;
  while(a!='1'&& a!='2')
  {
   cout<<"/n无效的输入!/n";
   cout<<"重新输入文件名(1)或返回主菜单(2)?";
   cin>>a;
  }
  if(a=='2')
   break;
  else
  {
   cout<<"/n请输入要压缩的文件名:";
   cin>>infilename;
   fopen_s(&infile,infilename,"rb");
  }
 }
 /*cout<<"/n输入压缩后的文件名:";
 cin>>outfilename;*/
 strcpy_s(outfilename,sizeof(outfilename),infilename);
 strcat_s(outfilename,sizeof(outfilename),".ty");
 fopen_s(&outfile,outfilename,"wb");
 if(outfile==NULL)
 {
  cout<<"/n压缩文件失败!无法创建压缩后的文件...";
  cout<<"/n按任意键回到主菜单...";
  _getch();
  return;
 }
 cout<<"压缩文件中...";
 //定义各变量
 int i,j,k;
 int used;                          //用到的字符数
 unsigned long total=0;              //文件长度

 //每个叶子节点赋值
 for(i=0;i<512;i++)
 {
  HTree[i].count=0;
  HTree[i].c=(unsigned char)i;
  HTree[i].lchild=-1;
  HTree[i].parent=-1;
  HTree[i].rchild=-1;
 }

 //统计各个字符出现次数
 unsigned char c;
 while(!feof(infile))
 {
  fread(&c,1,1,infile);
  HTree[c].count++;
  total++;
 }
 total--;
 HTree[c].count--;

 //count为0的不要,按count从大到小排列
 for(i=0;i<255;i++)
 {
  for(j=i+1;j<256;j++)
  {
   if(HTree[i].count<HTree[j].count)
   {
    temp=HTree[i];
    HTree[i]=HTree[j];
    HTree[j]=temp;
   }
  }
 }

 //有用字符数
 for(i=0;i<512;i++)
  if(HTree[i].count==0)
   break;
 used=i-1;
 
 //构建哈夫曼树
 unsigned long min;
 int m=2*i-1;
 int pt;
 for(i;i<m;i++)
 {
  min=3435973836;
  for(j=0;j<i;j++)
  {
   if(HTree[j].parent!=-1)
    continue;
   if(min>HTree[j].count)
   {
    pt=j;
    min=HTree[j].count;
   }
  }
  HTree[i].count=min;
  HTree[pt].parent=i;
  HTree[i].lchild=pt;
  min=3435973836;
  for(j=0;j<i;j++)
  {
   if(HTree[j].parent!=-1)
    continue;
   if(min>HTree[j].count)
   {
    pt=j;
    min=HTree[j].count;
   }
  }
  HTree[i].count+=min;
  HTree[pt].parent=i;
  HTree[i].rchild=pt;
 }

 //为每个有权值的字符编码
 for(i=0;i<=used;i++)
 {
  k=i;
  HTree[i].bits[0]=0;
  while(HTree[k].parent!=-1)
  {
   j=k;
   k=HTree[k].parent;
   if(HTree[k].lchild==j)
   {
    j=strlen(HTree[i].bits);
    memmove(HTree[i].bits+1,HTree[i].bits,j+1);
    HTree[i].bits[0]='0';
   }
   else
   {
    j=strlen(HTree[i].bits);
    memmove(HTree[i].bits+1,HTree[i].bits,j+1);
    HTree[i].bits[0]='1';
   }
  }
 }

 //写头文件
 char buf[512];
 fseek(infile,0,0);
 fwrite(&total,sizeof(unsigned long),1,outfile);          //原文件总长度
 fwrite(&used,sizeof(int),1,outfile);
 int maxCSize=0;             //最长的字符编码
 for(i=0;i<=used;i++)
 {
  if(maxCSize<strlen(HTree[i].bits))
   maxCSize=strlen(HTree[i].bits);
 }
 fwrite(&maxCSize,sizeof(int),1,outfile);
 for(i=0;i<=used;i++)
 {
  fwrite(&HTree[i].c,sizeof(unsigned char),1,outfile);
  fwrite(&HTree[i].bits,maxCSize,1,outfile);
 }

 //开始压缩主文件
 j=0;             //最大为total
 buf[0]=0;
 pt=12+5*used;
 while(!feof(infile))
 {
  c=fgetc(infile);
  j++;
  for(i=0;i<=used;i++)
  {
   if(HTree[i].c==c)
    break;
  }
  strcat(buf,HTree[i].bits);
  k=strlen(buf);
  c=0;
  while(k>=8)
  {
   for(i=0;i<8;i++)
   {
    if(buf[i]=='1')
     c=(c<<1)|1;
    else
     c=c<<1;
   }
   fwrite(&c,1,1,outfile);
   pt++;
   strcpy(buf,buf+8);
   k=strlen(buf);
  }
  if(j==total)
   break;
 }
 if(k>0)            //可能还有剩余字符
 {
  strcat(buf,"00000000");
  for(i=0;i<8;i++)
  {
   if(buf[i]=='1')
    c=(c<<1)|1;
   else
    c=c<<1;
  }
  fwrite(&c,1,1,outfile);
  pt++;
 }
 
 fclose(infile);
 fclose(outfile);
 cout<<"压缩成功!/n";
 float s;
 s=(float)pt/(float)total;
 cout<<"压缩率为:"<<s; 
 _getch();
 return;
}

/************解压缩**************/
void uncompress()
{
 FILE *infile,*outfile;
 char infilename[255],outfilename[255];
 cout<<"请输入要解压的文件名:";
 cin>>infilename;
 strcpy_s(outfilename,sizeof(outfilename),infilename);
 strcat_s(infilename,sizeof(infilename),".ty");
 fopen_s(&infile,infilename,"rb");
 while(infile==NULL)
 {
  char a;
  cout<<"文件"<<infilename<<"不存在.../n";
  cout<<"重新输入文件名(1)或返回主菜单(2)?";
  cin>>a;
  while(a!='1'&& a!='2')
  {
   cout<<"/n无效的输入!/n";
   cout<<"重新输入文件名(1)或返回主菜单(2)?";
   cin>>a;
  }
  if(a=='2')
   break;
  else
  {
   cout<<"/n请输入要解压的文件名:";
   cin>>infilename;
   strcat_s(infilename,sizeof(infilename),".ty");
   fopen_s(&infile,infilename,"rb");
  }
 }
 //cout<<"/n输入解压后的文件名:";
 //cin>>outfilename;
 fopen_s(&outfile,outfilename,"wb");
 if(outfile==NULL)
 {
  cout<<"/n解压文件失败!无法创建解压后的文件...";
  cout<<"/n按任意键回到主菜单...";
  _getch();
  return;
 }
 cout<<"解压文件中...";
 //开始解压
 int i,j,k,l;
 int used;                          //用到的字符数
 unsigned long total=0;              //文件长度
 char buf[256],bx[256];
 unsigned char c;
 int maxCSize;
 fread(&total,sizeof(unsigned long),1,infile);
 fread(&used,sizeof(int),1,infile);
 fread(&maxCSize,sizeof(int),1,infile);
 for(i=0;i<=used;i++)
 {
  fread(&HTree[i].c,sizeof(unsigned char),1,infile);
  fread(&HTree[i].bits,maxCSize,1,infile);
 }

 //开始解压
 j=0;
 bx[0]=0;
 while(1)
 {
  while(strlen(bx)<maxCSize)
  {
   fread(&c,1,1,infile);
   k=c;
   itoa(k,buf,2);
   k=strlen(buf);
   for(l=8;l>k;l--) //在单字节内对相应位置补0
   {
    strcat(bx,"0");
   }
   strcat(bx,buf);
  }
  for(i=0;i<=used;i++)
  {
   if(memcmp(HTree[i].bits,bx,strlen(HTree[i].bits))==0) break;
  }
  strcpy(bx,bx+strlen(HTree[i].bits));
  c=HTree[i].c;
  fwrite(&c,1,1,outfile);
  j++;   //统计解压缩后文件的长度
  if(j==total) break; 
 }
 fclose(infile);
 fclose(outfile);
 cout<<"解压成功!";
 cout<<"/n任意键返回主界面";
 _getch();
 return;
}

 

VS2005上编译成功的,大家有什么意见也欢迎回复。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值