这是学校的一篇程序设计题,哈夫曼编译码,里面包含一些对文件的操作
其中哈夫曼树的部分,是移植了网上一篇博主的代码,自己添加文件和译码的部分,下面做一些简单的说明。
1.#include
using namespace std;
这部分因为我用的编译器是vscode,在使用gets函数时报错,所以网上查到了加这个头文件,是C++的写法,cin.getline
2.因为作业要求,所以只写了a-h的译码,大家可以自行添加别的字符
3.文件操作放在D盘,大家也可以自行更改
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<iostream>
using namespace std;
#define x 7
#define n 8 //叶子数目
#define m (2*n-1) //结点总数
#define maxval 10000.0
#define maxsize 100 //哈夫曼编码的最大位数
typedef struct
{
char ch;
float weight;
int lchild,rchild,parent;
}hufmtree;
typedef struct
{
char bits[x]; //位串
int start; //编码在位串中的起始位置
char ch; //字符
}codetype;
char buf[maxsize]; /*数据文件缓冲区*/
char buf1[maxsize]; /*编码文件缓冲区*/
int num[maxsize]; /*字符个数*/
int len,length; /*字符长度和编码长度*/
//char temp[maxsize];
char table[maxsize]; /*字符内容*/
void huffman(hufmtree tree[]); //建立哈夫曼树
void huffmancode(codetype code[],hufmtree tree[]); //根据哈夫曼树求出哈夫曼编码
void decode(hufmtree tree[]); //依次读入电文,根据哈夫曼树译码
void PrintHFMT (hufmtree tree[]); //展示哈夫曼树
void OpenFile (void); //打开数据文件
void ReadFile(void); //读取编码文件
void savecode(codetype code[]); //保存编码文件
void savetobe(codetype code[],hufmtree tree[]); //保存原文文件
int main()
{
printf("总共有%d种字符\n",n);
hufmtree tree[m];
codetype code[n];
int flag;
printf(".................哈夫曼编码系统..............\n");
printf(".................1.读取数据文件..............\n");
printf(".................2.编码并保存................\n");
printf(".................3.读取编码文件..............\n");
printf(".................4.解码并保存................\n");
printf(".................5.展示哈夫曼树..............\n");
printf(".................6.退出代码..................\n");
while(1)
{
scanf("%d",&flag);
if(flag == 1)
{
OpenFile ();
}
if(flag == 2)
{
huffman(tree);//建立哈夫曼树
huffmancode(code,tree);//根据哈夫曼树求出哈夫曼编码
printf("【输出每个字符的哈夫曼编码】\n");
savecode(code);
}
if(flag == 3)
{
ReadFile();
}
if(flag == 4)
{
savetobe(code,tree);
}
if(flag == 5)
{
PrintHFMT(tree);
}
if(flag == 6)
{
exit(1);
}
}
//decode(tree);//依次读入电文,根据哈夫曼树译码
return 0;
}
void OpenFile (void)
{
int num_a = 0,num_b = 0,num_c = 0;
//char buf[MAX_LINE]; /*缓冲区*/
FILE* fp; /*文件指针*/
if ((fp = fopen("D://DataFile.txt", "r")) == NULL)
{
perror("fail to read");
exit(1);
}
while (fgets(buf, maxsize, fp) != NULL)
{
len = strlen(buf);
buf[len] = '\0'; /*去掉换行符*/
printf("数据文件:%s 长度:%d\n", buf,len);
}
for(int i=0;buf[i];i++)
{
if(buf[i] == 'a')
{
num[0]++;
table[0] = 'a';
}
if(buf[i] == 'b')
{
num[1]++;
table[1] = 'b';
}
if(buf[i] == 'c')
{
num[2]++;
table[2] = 'c';
}
if(buf[i] == 'd')
{
num[3]++;
table[3] = 'd';
}
if(buf[i] == 'e')
{
num[4]++;
table[4]='e';
}
if(buf[i] == 'f')
{
num[5]++;
table[5]='f';
}
if(buf[i] == 'g')
{
num[6]++;
table[6] = 'g';
}
if(buf[i] == 'h')
{
num[7]++;
table[7] = 'h';
}
}
}
void ReadFile(void)
{
// int length = 0;
FILE *fp;
fp = fopen("D://CodeFile.txt", "r");
if(fp == NULL)
{
printf("Error!");
exit(1);
}
while (fgets(buf1, maxsize, fp) != NULL)
{
length = strlen(buf1);
buf1[length] = '\0'; /*去掉换行符*/
printf("读到的编码:%s 编码长度:%d\n", buf1,length );
}
fclose(fp);
}
void savecode(codetype code[])
{
FILE *fpn;
fpn = fopen("D://Codefile.txt", "w");
if(fpn == NULL)
{
printf("Error!");
exit(1);
}
for(int i=0;i<n;i++)
{
//printf("%c: ",code[i].ch);
for(int j=code[i].start;j<n;j++)
{
printf("%c",code[i].bits[j]);
//temp[j] = code[i].bits[j];
//printf("%c",temp[j]);
fputc(code[i].bits[j],fpn);
}
}
printf("\n");
fclose(fpn);
}
void huffman(hufmtree tree[])//建立哈夫曼树
{
int i,j,p1,p2;//p1,p2分别记住每次合并时权值最小和次小的两个根结点的下标
float small1,small2,f;
char c;
for(i=0;i<m;i++) //初始化
{
tree[i].parent=0;
tree[i].lchild=-1;
tree[i].rchild=-1;
tree[i].weight=0.0;
}
//printf("【依次读入前%d个结点的字符及权值(中间用空格隔开)】\n",n);
for(i=0;i<n;i++) //读入前n个结点的字符及权值
{
// printf("输入第%d个字符为和权值",i+1);
// scanf("%s",&c);
//getchar();
tree[i].ch=table[i];
tree[i].weight=num[i];
}
for(i=n;i<m;i++) //进行n-1次合并,产生n-1个新结点
{
p1=0;p2=0;
small1=maxval;small2=maxval; //maxval是float类型的最大值
for(j=0;j<i;j++) //选出两个权值最小的根结点
if(tree[j].parent==0)
if(tree[j].weight<small1)
{
small2=small1; //改变最小权、次小权及对应的位置
small1=tree[j].weight;
p2=p1;
p1=j;
}
else
if(tree[j].weight<small2)
{
small2=tree[j].weight; //改变次小权及位置
p2=j;
}
tree[p1].parent=i;
tree[p2].parent=i;
tree[i].lchild=p1; //最小权根结点是新结点的左孩子
tree[i].rchild=p2; //次小权根结点是新结点的右孩子
tree[i].weight=tree[p1].weight+tree[p2].weight;
}
}//huffman
void PrintHFMT (hufmtree tree[]) /*输出向量状态表*/
{
int i,k=0;
printf("\n哈夫曼树的各边显示:\n");
for (i=0; i<2*n-1; i++)
while(tree[i].lchild!=-1)
{
printf("(%.0f,%d),(%.0f,%d)\n",tree[i].weight,tree[i].lchild,tree[i].weight,tree[i].rchild);
break;
}
}
void huffmancode(codetype code[],hufmtree tree[])//根据哈夫曼树求出哈夫曼编码
//codetype code[]为求出的哈夫曼编码
//hufmtree tree[]为已知的哈夫曼树
{
int i,c,p;
codetype cd; //缓冲变量
for(i=0;i<n;i++)
{
cd.start=n;
cd.ch=tree[i].ch;
c=i; //从叶结点出发向上回溯
p=tree[i].parent; //tree[p]是tree[i]的双亲
while(p!=0)
{
cd.start--;
if(tree[p].lchild==c)
cd.bits[cd.start]='0'; //tree[i]是左子树,生成代码'0'
else
cd.bits[cd.start]='1'; //tree[i]是右子树,生成代码'1'
c=p;
p=tree[p].parent;
}
code[i]=cd; //第i+1个字符的编码存入code[i]
}
}//huffmancode
void decode(hufmtree tree[])//依次读入电文,根据哈夫曼树译码
{
int i,j=0;
char b[maxsize];
char endflag='2'; //电文结束标志取2
i=m-1; //从根结点开始往下搜索
// printf("输入发送的编码(以'2'为结束标志):");
//cin.getline(buf,20);
for(int i = 0; i < length; i++)
{
b[i]=buf1[i];
}
b[length+1] = '2';
printf("译码后的字符为");
while(b[j]!='2')
//for(j = 0; j < 12; )
{
if(b[j]=='0')
i=tree[i].lchild; //走向左孩子
else
i=tree[i].rchild; //走向右孩子
if(tree[i].lchild==-1) //tree[i]是叶结点
{
printf("%c",tree[i].ch);
i=m-1; //回到根结点
}
j++;
}
printf("\n");
if(tree[i].lchild!=-1&&buf[j]!='2') //电文读完,但尚未到叶子结点
//if(j > length || j < length)
printf("\nERROR\n"); //输入电文有错
}
void savetobe(codetype code[],hufmtree tree[])
{
char b[maxsize];
FILE *fpn;
fpn = fopen("D://ToBeTran.txt", "w");
if(fpn == NULL)
{
printf("Error!");
exit(1);
}
int i,j=0;
char endflag='2'; //电文结束标志取2
i=m-1; //从根结点开始往下搜索
//printf("输入发送的编码(以'2'为结束标志):");
//cin.getline(b,length);
for(int i = 0; i < length; i++)
{
b[i]=buf1[i];
}
b[length + 1] = '2';
printf("译码后的字符为");
while(b[j]!='2')
//for(j = 0; j < length; )
{
if(b[j]=='0')
i=tree[i].lchild; //走向左孩子
else
i=tree[i].rchild; //走向右孩子
if(tree[i].lchild==-1) //tree[i]是叶结点
{
// printf("%c",tree[i].ch);
if(tree[i].ch == 'a')
{
for(int k = 0; k < num[0]; k++)
{
printf("a");
fputc('a',fpn);
}
}
if(tree[i].ch == 'b')
{
for(int k = 0; k < num[1]; k++)
{
printf("b");
fputc('b',fpn);
}
}
if(tree[i].ch == 'c')
{
for(int k = 0; k < num[2]; k++)
{
printf("c");
fputc('c',fpn);
}
}
if(tree[i].ch == 'd')
{
for(int k = 0; k < num[3]; k++)
{
printf("d");
fputc('d',fpn);
}
}
if(tree[i].ch == 'e')
{
for(int k = 0; k < num[4]; k++)
{
printf("e");
fputc('e',fpn);
}
}
if(tree[i].ch == 'f')
{
for(int k = 0; k < num[5]; k++)
{
printf("f");
fputc('f',fpn);
}
}
if(tree[i].ch == 'g')
{
for(int k = 0; k < num[6]; k++)
{
printf("g");
fputc('g',fpn);
}
}
if(tree[i].ch == 'h')
{
for(int k = 0; k < num[7]; k++)
{
printf("h");
fputc('h',fpn);
}
}
i=m-1; //回到根结点
}
j++;
}
}