开发环境:
Visual C++6.0
代码:
#include <iostream>
#include <windows.h>
#include <fstream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
typedef struct
{
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef struct
{
char* data;
int* num;
int length;
}TNode;
typedef struct
{
char *data;
char** HM;
}Code;
typedef char** HuffmanCode;
bool IfEncode()
{
int choose;
cout<<"\t 1.加密压缩 2.无密压缩"<<endl;
cout<<"请输入选择:";
cin>>choose;
if(choose==1)
return true;
else
return false;
}
void Encode(vector<char>& v)
{
char ch[30];
v.push_back('@');
cout<<"请输入压缩密码"<<endl;
cin>>ch;
for(int i=0;ch[i]!='\0';i++)
v.push_back(ch[i]);
v.push_back('\0');
v.push_back('@');
cout<<"开始压缩…"<<endl;
}
void ReadTxt(vector<char>& v)
{
char ch;
ifstream infile("vae.txt",ios::in);//读取需要压缩的文件vae.txt
if(!infile)
{
cerr<<"open error"<<endl;
exit(1);
}
if(IfEncode())
Encode(v);
while(infile.peek()!=EOF)
{
infile.get(ch);
v.push_back(ch);
}
infile.close();
}
void InitList(TNode& T)
{
T.data=new char[256];
T.num=new int[256];
if(!T.data||!T.num)
exit(1);
T.length=0;
}
bool Find(TNode T,char ch)
{
int i;
for(i=0;i<T.length;i++)
if(ch==T.data[i])
return true;
return false;
}
void TCount(vector<char> v1,TNode &T)
{
int i,j=0;
char ch;
int m=v1.size();
for(i=0;i<m;i++)
{
ch=v1[i];
if(!Find(T,ch))
{
T.data[j]=ch;
T.num[j]=count(v1.begin(),v1.end(),ch);
j++;
T.length++;
}
}
}
void Select(HuffmanTree &HT,int m,int& s1,int& s2)
{
int k,j,n,min=32767;
for(k=1;k<=m;k++)s
if(HT[k].weight<=min)
{
j=k;
min=HT[k].weight;
}
}
s1=j;
HT[j].parent=1;
min=32767;
for(k=1;k<=m;k++)
{
if(HT[k].parent==0)
if(HT[k].weight<=min)
{
n=k;
min=HT[k].weight;
}
}
s2=n;
}
void CreateHuffmanTree (HuffmanTree &HT,TNode T,int length)
{
int m,i,s1,s2;
if(length<=1)
return;
m=2*length-1;
HT=new HTNode[m+1];
for(i=1;i<=m;++i)
{
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for(i=1;i<=length;++i)
HT[i].weight=T.num[i-1];
for(i=length+1;i<=m;i++)
{
Select(HT,i-1,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;
}
}
void CreatHuffmanCode (HuffmanTree HT,HuffmanCode &HC,int n)
{
int i,f,c,start;
HC=new char*[n+1];
char* cd=new char[n];
cd[n-1]='\0';
for(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]);
}
delete cd;
}
void Zip(HuffmanCode HC,vector<char> v,TNode T)
{
int i=0,j=0,k=0;
ofstream outfile("com.zip",ios::out);//输出压缩后的文件com.zip
if(!outfile)
{
cerr<<"open error"<<endl;
exit(1);
}
for(i=0;i<v.size();i++)
{
for(j=0;j<T.length;j++)
if(T.data[j]==v[i])
break;
for(k=0;HC[j+1][k]!='\0';k++)
outfile<<HC[j+1][k];
}
outfile.close();
cout<<"正在压缩…";Sleep(500);cout<<" 。";Sleep(500);cout<<" 。"<<endl;
cout<<"压缩成功,可到com.zip中查看压缩后文件"<<endl;
}
bool REncode(char ch[])
{
int i=0;
char code[30];
cout<<"请输入解压密码:"<<endl;
cin>>code;
if(strcmp(code,ch)==0)
return true;
else
return false;
}
int in_HM(Code &c)
{
c.data=new char[256];
c.HM=new char* [256];
int length=0,i;
ifstream infile("code.dat",ios::in);
if(!infile)
{
cerr<<"open error"<<endl;
exit(1);
}
infile>>length;
for(i=0;i<length;i++)
{
infile>>c.data[i];
c.HM[i]=new char[length+1];
infile>>c.HM[i];
}
infile.close();
return length;
}
void out_HM(HuffmanCode HC,TNode T)
{
int i;
ofstream outfile("code.dat",ios::out);
if(!outfile)
{
cerr<<"open error"<<endl;
exit(1);
}
outfile<<T.length<<endl;
for(i=0;i<T.length;i++)
{
outfile<<T.data[i]<<endl;
outfile<<HC[i+1]<<endl;
}
outfile.close();
}
void RZip(HuffmanCode HC,TNode T)
{
char ch;
char ch2[30];
int i,j,flag,flag2=0,m=0;
ofstream outfile("rzip.txt",ios::out);//将压缩好的文件重新解压为rzip.txt
ifstream infile("com.zip",ios::in);
if(!outfile)
{
cerr<<"open error"<<endl;
exit(1);
}
if(!infile)
{
cerr<<"open error"<<endl;
exit(1);
}
while(infile.peek()!=EOF)
{
flag=0;
char* cd=new char[T.length];
for(i=0;;i++)
{
infile>>ch;
cd[i]=ch;
cd[i+1]='\0';
for(j=1;j<=T.length;j++)
if(strcmp(HC[j],cd)==0)
{
if(T.data[j-1]=='@')
{
if(flag2==0)
{
flag=1;
flag2=1;
delete cd;
break;
}
if(flag2==1)
{
while(1)
{
if(REncode(ch2))
{
cout<<"密码正确,正在解压… "<<endl;
flag2=0;
break;
}
else
cout<<"密码错误,请重新输入"<<endl;
}
flag=1;
delete cd;
break;
}
}
if(flag2==1)
{
ch2[m]=T.data[j-1];
flag=1;
m++;
delete cd;
break;
}
if(flag2==0)
{
outfile<<T.data[j-1];
flag=1;
delete cd;
break;
}
}
if(flag==1)
break;
}
}
cout<<"正在解压…";Sleep(500);cout<<" 。";Sleep(500);cout<<" 。"<<endl;
cout<<"解压成功!请到rzip.txt中查看解压后文件" <<endl;
}
void RZip()
{
char ch;
char ch2[30];
Code c;
int i,j,length,flag,flag2=0,m=0;
length=in_HM(c);
ofstream outfile("rzip.txt",ios::out);
ifstream infile("com.zip",ios::in);
if(!outfile)
{
cerr<<"open error"<<endl;
exit(1);
}
if(!infile)
{
cerr<<"open error"<<endl;
exit(1);
}
while(infile.peek()!=EOF)
{
flag=0;
char* cd=new char[length];
for(i=0;;i++)
{
infile>>ch;
cd[i]=ch;
cd[i+1]='\0';
for(j=0;j<length;j++)
if(strcmp(c.HM[j],cd)==0)
{
if(c.data[j]=='@')
{
if(flag2==0)
{
flag=1;
flag2=1;
delete cd;
break;
}
if(flag2==1)
{
while(1)
{
if(REncode(ch2))
{
cout<<"密码正确,正在解压… "<<endl;
flag2=0;
break;
}
else
cout<<"密码错误,请重新输入"<<endl;
}
flag=1;
delete cd;
break;
}
}
if(flag2==1)
{
ch2[m]=c.data[j];
flag=1;
m++;
delete cd;
break;
}
if(flag2==0)
{
outfile<<c.data[j];
flag=1;
delete cd;
break;
}
}
if(flag==1)
break;
}
}
cout<<"正在解压…";Sleep(500);cout<<" 。";Sleep(500);cout<<" 。"<<endl;
cout<<"解压成功,请到rzip.txt中查看解压后的文件" <<endl;
}
void about()
{
system("cls");
cout<<"\t╔═══════════════════════════════╗"<<endl;
cout<<"\t║ 欢迎使用解压缩程序 ^-^ ║"<<endl;
cout<<"\t║ 关于程序: ║"<<endl;
cout<<"\t║ 压缩: ║"<<endl;
cout<<"\t║ 默认压缩程序所在目录下的vae.txt,生成压缩后的com.zip ║"<<endl;
cout<<"\t║ ║"<<endl;
cout<<"\t║ 解压: ║"<<endl;
cout<<"\t║ 默认解压序所在目录下的com.zip,生成解压后的rzip.txt ║"<<endl;
cout<<"\t║ ║"<<endl;
cout<<"\t║ 无源文件解压: ║"<<endl;
cout<<"\t║ 使用仅以压缩文件解压,无需源文件,仅支持无空格无回车 ║"<<endl;
cout<<"\t║ 的文本解压! ║"<<endl;
cout<<"\t║ ║"<<endl;
cout<<"\t║ 程序支持压缩中英文文件。 ║"<<endl;
cout<<"\t║ ║"<<endl;
cout<<"\t╚═══════════════════════════════╝"<<endl;
}
void menu()
{
cout<<"\t╔═══════════════════════════════╗"<<endl;
cout<<"\t║ ║"<<endl;
cout<<"\t║ 欢迎使用解压缩程序 ║"<<endl;
cout<<"\t║ ║"<<endl;
cout<<"\t║ 1. 压缩文件 ║"<<endl;
cout<<"\t║ 2. 解压文件 ║"<<endl;
cout<<"\t║ 3. 无源文件解压 ║"<<endl;
cout<<"\t║ 4. 关于程序 ║"<<endl;
cout<<"\t║ 5. 退出程序 ║"<<endl;
cout<<"\t║ ║"<<endl;
cout<<"\t╚═══════════════════════════════╝"<<endl;
}
int main(){
vector<char> v;
int n,choose;
TNode T;
InitList(T);
HuffmanTree HT;
HuffmanCode HC;
while(1)
{
system("cls");
menu();
cout<<"请输入所选择的序号:";
cin>>choose;
switch(choose)
{
case 1:
ReadTxt(v);
TCount(v,T);
n=T.length;
CreateHuffmanTree (HT,T,n);
CreatHuffmanCode (HT,HC,n);
Zip(HC,v,T);
out_HM(HC,T);
system("pause");
break;
case 2:
RZip(HC,T);
system("pause");
break;
case 3:
RZip();
system("pause");
break;
case 4:
about();
system("pause");
break;
case 5:
return 0;
default:
cout<<"输入错误,请重新输入";
system("pause");
break;
}
}
return 0;
}
实验步骤:
运行选项4进入使用说明
原理:
哈夫曼编码的思想对文件进行压缩,主要原理是通过哈夫曼编码来重新表示字符,使得出现频率高的字符编码短,出现少的字符编码长。
压缩过程:
1)统计字符种类及频度:ASCII码值当下标,字符频度当对应下标的值。
2)构建哈夫曼树:在没有访问过的节点中,找最小字符频度下标来构建哈夫曼树。
3)构建哈夫曼编码:递归思想构建。
4)生成压缩文件(.ycyHuf后缀名):把字符的哈夫曼编码以二进制形式写入目标文件中。给压缩文件头部写入元数据,解压缩时需使用这些数据。
解压过程:
1)根据压缩文件的头部元数据,得到字符种类和频度。
2)构建哈夫曼树:在没有访问过的节点中,找最小字符频度下标来构建哈夫曼树。
3)构建哈夫曼编码:递归思想构建。
4)生成解压缩的文件(后缀名和源文件后缀名一样 )即可:一位一位的从压缩文件中读取信息,'1’向左子树走,'0’向右子树走。