本人小辣鸡一枚,学校的一个数据结构作业。在此记录。
编译器:codeblocks
功能:
1.选择文件建立哈夫曼树
2.建立密码本,对文件进行编码
3.选择需要进行解码的文件解码
4.按位压缩方式压缩与解压,并且显示压缩比
运行操作及效果
目录结构:
11.txt 必须有,剩下的随意,可以最初没有。让11.txt和自己的cpp文件放到一个文件夹里面。11.txt 里面随便写点什么,我写的内容是:
bbbbbbb
AAAAAA
cc
ddddd
eeeeeeeee
下面是我的运行过程,运行完之后,22.txt存的是编码文件,33.txt存的是编码文件解码后的文件,经过验证,和11.txt内容应该相同。44.txt是22.txt按位压缩后的结果,55.txt是44.txt解码后的结果,应该和22.txt内容相同。最后得到的压缩比应该大概是1/8
#include<windows.h>
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<fstream>
using namespace std;
typedef struct HTNode{
char data;
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode;
char myarr[1000]={NULL};
int mcnt;
int pData;//原文件
int nData;//压缩后的的文件
int multiple;
/*******************************************选择文件**************************************/
void ChooseFile(){
char n[20];
FILE *fp;
char ch;
int i=1;
cout<<"输入要打开的文件"<<endl;
cin>>n;
fp=fopen(n,"r");
if(fp==NULL)
printf("打开失败!\n");
else{
fscanf(fp,"%c",&ch);
while(!feof(fp)){
myarr[i]=ch;
fscanf(fp,"%c",&ch);
i++;
}
fclose(fp);
myarr[i]='\0';
}
pData=i-1;
cout<<"打开原始文件"<<n<<"成功!";
cout<<endl;
}
/*******************************************求文件中叶子结点的权值**************************************/
void Count(HuffmanTree &HT){//统计
mcnt=0;
int cnt[256]={0};
for (int i=1;myarr[i]!='\0';i++)
cnt[myarr[i]]++;
for (int i=0;i<256;i++){
if(cnt[i]>0){
mcnt++;
}
}
int m=2*mcnt-1;
HT=new HTNode[m+1];
int mcnt2=0;
for (int i=0;i<256;i++){
if(cnt[i]>0){
mcnt2++;
//cout<<"字符"<<(char)i<<"出现了"<<cnt[i]<<"次"<<endl;
HT[mcnt2].weight=cnt[i];
HT[mcnt2].data=(char)i;
}
}
}
/*****************************************哈夫曼编码******************************************************/
void HuffmanCODE(HuffmanTree HT, HuffmanCode &HC, int n){
HC=new char *[n+1];
char * cd=new char[n];
cd[n-1]='\0';
int start,c,f;
for(int 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]);
}
for(int i=1;i<=mcnt;i++){
cout<<HT[i].data<<"出现频度是:"<<HT[i].weight<<" 编码为"<<HC[i]<<endl;
}
delete cd;
ofstream file;
char fl[100];
cout<<"输入要写入的文件"<<endl;
cin>>fl;
file.open(fl,ios::out);
for(int i=1;myarr[i]!='\0';i++){
for(int j=1;j<=mcnt;j++){
if(myarr[i]==HT[j].data){
//hc[j]字符串数组
file <<HC[j];
break;
}
}
}
file.close();
}
/*****************************************哈夫曼译码******************************************************/
void HuffmanDECODE(HuffmanTree HT,int n){
char decode[1000];
FILE *fp; //创建一个文件指针*fp
char ch;
int mcnt3=0;
char f1[20];
cout<<endl<<"输入要解码哪个文件"<<endl;
cin>>f1;
fp=fopen(f1,"r");
if(fp==NULL)
printf("打开失败!\n");
else{
int i=0;
fscanf(fp,"%c",&ch);
while(!feof(fp)){
decode[i]=ch;
// putchar(ch);
fscanf(fp,"%c",&ch);
i++;
mcnt3++;
}
fclose(fp);
decode[i]='\0';
}
cout<<endl;
ofstream file;
char fl[100];
cout<<"输入要写入的文件"<<endl;
cin>>fl;
file.open(fl, ios::out);
int i,p;
p=2*n-1;
for(i=0;i<=mcnt3;i++){
if(HT[p].lchild==0&&HT[p].rchild==0){
// printf("%c",HT[p].data);
file <<HT[p].data;
p=2*n-1;
}
if(decode[i]=='0')
p=HT[p].lchild;
else if(decode[i]=='1')
p=HT[p].rchild;
}
file.close();
printf("译码成功\n");
}
/********************************找到父节点不为0的两个叶子结点**********************************/
void Select(HuffmanTree &HT,int i,int &s1,int &s2){
s1=1;
s2=1;
int sm1=9999;
int sm2=9999;
for(int j=1;j<i;j++){
if(HT[j].parent==0){
if(HT[j].weight<sm1){
sm2=sm1;
sm1=HT[j].weight;
s2=s1;
s1=j;
}
else if(HT[j].weight<sm2){
sm2=HT[j].weight;
s2=j;
}
}
}
}
/****************************************创建哈夫曼树**************************************/
void CreatHuffmanTree(HuffmanTree &HT){
int m=2*mcnt-1;
for(int i=1;i<=m;i++){
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
if(i>mcnt){
HT[i].weight=0;
}
}
for(int i=mcnt+1;i<=m;i++){
int s1=1,s2=1;
Select(HT,i,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;
}
cout<<"创建哈夫曼树成功!"<<endl;
cout<<"字符 "<<"权值 "<<"双亲 "<<"左孩子 "<<"右孩子 "<<endl;
for(int i=1;i<=m;i++){
if(i<=mcnt){
cout<<HT[i].data<<" ";
}
else{
cout<<" ";
}
cout<<HT[i].weight<<" ";
cout<<HT[i].parent<<" ";
cout<<HT[i].lchild<<" ";
cout<<HT[i].rchild<<" ";
cout<<endl;
}
}
/******************************二进制转十进制************************/
int binary2decimal(char str[]){
int sum=0;
int j=1;
int pos = strlen(str) - 1;
for(;pos>=0;pos--)
{
sum+=(str[pos]-'0')*j;
j*=2;
}
return sum;
}
/*********************************压缩*********************************/
void Compress(){
int mcount;
char decode[1000];
FILE *fp; //创建一个文件指针*fp
char ch;
char f1[20];
int mcnt3=0;//存储有多少个1 0
cout<<endl<<"输入要打开哪个编码文件进行压缩?"<<endl;
cin>>f1;
fp=fopen(f1,"r");
cout<<endl;
if(fp==NULL)
printf("打开失败!\n");
else{
// cout<<"编码文件的内容是:"<<endl;
int i=0;
fscanf(fp,"%c",&ch);
while(!feof(fp)){
decode[i]=ch;
// putchar(ch);
fscanf(fp,"%c",&ch);
i++;
mcnt3++;
}
fclose(fp);
decode[i]='\0';
}
cout<<endl;
//已经实现把所有二进制都读入到decode数组里了,下标从0开始
//补齐0
int k;
if(mcnt3%8!=0){
multiple=8-mcnt3%8;
for(k=0;k<multiple;k++){
decode[mcnt3+k]='0';
}
decode[mcnt3+k]='\0';
}
char data[10];
data[8]='\0';
ofstream file;
char fl[100];//解压后.txt
cout<<"输入把编码文件压缩到哪个文件"<<endl;
cin>>fl;
file.open(fl,ios::out|ios::binary);
for(mcount=0;mcount<mcnt3+k;mcount++){
data[mcount%8]=decode[mcount];//8个存到data数组
if((mcount+1)%8==0){
int result=binary2decimal(data);//变成十进制
char res=(char)result;
file<<res;
}
}
file.close();
}
/*****************************十进制转二进制**************************/
int decimal2binary(int x,int &flag,int &digit){
int p=1,y=0,yushu;
int cont=0;
while(1)
{
yushu=x%2;
x/=2;
y+=yushu*p;
p*=10;
if(x<2)
{
y+=x*p;
break;
}
}
int temp=y;
while(temp!=0)
{
temp/=10;
cont++;
}
if(cont!=8){
flag=1;
digit=8-cont;
}
return y;
}
/*******************************解压*****************************/
void Decompression(){
int decode[1000];
FILE *fp; //创建一个文件指针*fp
char ch;
char f1[20];
int mcnt3=0;//存储有多少个1 0
cout<<endl<<"输入要解压哪个文件?"<<endl;
cin>>f1;
fp=fopen(f1,"r");
cout<<endl;
if(fp==NULL)
printf("打开失败!\n");
else{
int i=0;
fscanf(fp,"%c",&ch);
while(!feof(fp)){
decode[i]=ch;
int data =ch;
if(data<0){
data=256+data;
decode[i]=data;
//mcnt3++;
}
fscanf(fp,"%c",&ch);
i++;
mcnt3++;
}
decode[i]='\0';
nData=i;
fclose(fp);
}
ofstream file;
char fl[100];//解压后.txt
cout<<endl<<"输入解压后的编码文件存在哪里?"<<endl;
cin>>fl;
file.open(fl,ios::out|ios::binary);
char temp[1000];
for(int i=0;decode[i]!='\0';i++){
int flag=0;
int dight=0;
int res=decimal2binary(decode[i],flag,dight);
if(flag==1){
for(int y=1;y<=dight;y++){
char c='0';
file<<c;
}
}
file<<res;
}
file.close();
FILE *fp2;
fp2=fopen(fl,"r");
cout<<endl;
if(fp2==NULL)
printf("打开失败!\n");
else{
int i=0;
fscanf(fp2,"%c",&ch);
while(!feof(fp2)){
if(i<(mcnt3)*8-multiple){
temp[i]=ch;
fscanf(fp2,"%c",&ch);
i++;
}
else{
break;
}
}
fclose(fp2);
temp[i]='\0';
}
ofstream file2;
file2.open(fl,ios::out|ios::binary);
for(int i=0;temp[i]!='\0';i++){
file2<<temp[i];
}
file2.close();
}
void setColor(unsigned short ForeColor=4,unsigned short BackGroundColor=15){
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);//获取当前窗口句柄
SetConsoleTextAttribute(handle,ForeColor+BackGroundColor*0x10);//设置颜色
}
/*********************************压缩比*********************************/
void CompressionRatio(){
cout<<"输入压缩前的文件是?"<<endl;
ChooseFile();
cout<<endl;
int decode[1000];
FILE *fp; //创建一个文件指针*fp
char ch;
char f1[20];
int mcnt3=0;//存储有多少个1 0
cout<<endl<<"输入压缩后的文件是?"<<endl;
cin>>f1;
fp=fopen(f1,"r");
cout<<endl;
if(fp==NULL)
printf("打开失败!\n");
else{
int i=0;
fscanf(fp,"%c",&ch);
while(!feof(fp)){
decode[i]=ch;
int data =ch;
if(data<0){
data=256+data;
decode[i]=data;
//mcnt3++;
}
fscanf(fp,"%c",&ch);
i++;
mcnt3++;
}
decode[i]='\0';
nData=i;
fclose(fp);
}
double raTio=1.0*nData/pData*100;
cout<<"压缩比是:"<<nData<<"/"<<pData<<"="<<raTio<<"%"<<endl;
}
void HomePage(){
cout<<"========================================================"<<endl;
cout<<"* *哈夫曼编码译码器* *"<<endl;
cout<<"* 1、选择需要进行编码的文件 *"<<endl;
cout<<"* 2、建立哈夫曼树 *"<<endl;
cout<<"* 3、建立密码本并对文件编码 *"<<endl;
cout<<"* 4、选择需要进行解码的文件并解码 *"<<endl;
cout<<"* 5、按位压缩方式对文件进行压缩 *"<<endl;
cout<<"* 6、显示压缩比 *"<<endl;
cout<<"* 7、解压 *"<<endl;
cout<<"* 8、退出 *"<<endl;
cout<<"========================================================"<<endl;
}
main(){
setColor();
//system("color 3");
HuffmanTree hf;
HuffmanCode hc;
while(1){
HomePage();
cout<<"输入您想进行的操作是(1-8):";
int n;
cin>>n;
if(n==1){
ChooseFile();//第一个功能
}else if(n==2){
Count(hf);
CreatHuffmanTree(hf);//第二个功能
}else if(n==3){
HuffmanCODE(hf,hc,mcnt);//第三个功能
}else if(n==4){
HuffmanDECODE(hf,mcnt);//第四个功能
}else if(n==5){
Compress();//压缩
}else if(n==6){
CompressionRatio();
}else if(n==7){
Decompression();
}else if(n==8){
return 0;
}else{
cout<<"输入错误!";
}
system("pause");
system("cls");
}
}
哈夫曼树网上有很多的讲解。
压缩和解压的代码不多。。。
8位是256个(0-255)。。。然后char存字符。。存0-255的数字,刚好是8位,2的8次方。
所以把编码后的每8位都变成一个十进制的数字,存在char中,然后翻译再对照后翻译回十进制,再变成二进制。。。如果最后不足八位,补齐0,补成8位,解码时候注意去掉0。
欢迎各位大佬多多指教。