这是一道数据结构的实验题。
实验三&四 哈夫曼编/译码器
一、实验目的
掌握哈夫曼编码基本运算以及存储结构表示。
二、实验内容:
1. 系统要求包含以下功能
1)初始化:从终端读入字符集大小n,以及n个字符和n个权值(或者读入字符集和频度数据文件),建立哈夫曼树,并将哈夫曼树存入到文件HfmTree中。
2)编码:利用已建好的哈夫曼树(如果不在内存中,则从文件中读入),从文件ToBeTran中读入原文,对原文进行编码,将编码后的结果存入文件CodeFile中。
3)译码:利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
4)打印:打印输出哈夫曼树,显示ToBeTran, TextFile和CodeFile文件的内容。
三、实验要求:
- 能在机器上正确运行程序;
2. 用户界面友好。
四、测试数据:
- 字符集和频度数据(建树依据)
字符 | 空格 | A | B | C | D | E | F | G | H |
频度 | 186 | 64 | 13 | 22 | 32 | 103 | 21 | 15 | 47 |
字符 | I | J | K | L | M | N | O | P | Q |
频度 | 57 | 1 | 5 | 32 | 20 | 57 | 63 | 15 | 1 |
字符 | R | S | T | U | V | W | X | Y | Z |
频度 | 48 | 51 | 80 | 23 | 8 | 18 | 1 | 16 | 1 |
- 原文内容“THIS IS MY PROGRAM
#define OK 1
#define ERROR 0
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct {
int weight;
int parent, lchild, rchild;
char value;
} HTNode, * HuffmanTree;
typedef char** HuffmanCode;
int m;//赫夫曼树节点数
void Select(HuffmanTree HT, int n, int& s1, int& s2) {
int min = n;
for (int i = 1; i <= n; i++)
{
if (HT[i].parent != 0) continue;
else {
if (HT[i].weight <= HT[min].weight) min = i;
}
}
s1 = min; min = 1;
while (HT[min].parent != 0 || min == s1) min++;
for (int i = 1; i <= n; i++)
{
if (HT[i].parent != 0||i==s1) continue;
else {
if (HT[i].weight <= HT[min].weight) min = i;
}
}
s2 = min;
}
void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n,char *c) {
FILE* fp1, * fp2;
int f,i;
fp1 = fopen("HfmTree.txt", "w+");
fp2 = fopen("CodeFile.txt", "w+");
HuffmanTree p;
if (n <= 1) return;
m = 2 * n - 1;
HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));//0号单元未用
for (p = HT + 1, i = 1; i <= n; ++i, ++p, ++w,++c) *p = { *w,0,0,0,*c };
for (; i <= m; ++i, ++p) *p = { 0,0,0,0,'0' };
for (i = n + 1; i <= m; ++i) {//建赫夫曼树
//在HT[1..i-1]选择parent为0且weight最小的两个结点,其序号分别为s1,s2
int s1, s2;
Select(HT, i - 1, s1, s2);
//printf("%d\t%d\t%d\t%d\n", HT[s1].weight, HT[s2].weight,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;
}
for (int i = 1; i <= m; i++) {
fprintf(fp1, "%d %c\t\t%d\t%d\t%d\t%d\n",i,HT[i].value, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
}
fclose(fp1);
//从叶子到根逆向求每个字符的赫夫曼编码
HC = (HuffmanCode)malloc((n + 1) * sizeof(char*));//分配n个字符编码的头指针向量
char* cd = (char*)malloc(n * sizeof(char));//分配求编码的工作空间
cd[n - 1] = '\0';
for (i = 1; i <= n; ++i) {
int start = n - 1;
for (int c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent)
{
if (HT[f].lchild == c) cd[--start] = '0';
else cd[--start] = '1';
}
HC[i] = (char*)malloc((n - start) * sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i], &cd[start]);
}
free(cd);
for (int i = 1; i <= n ; i++)
{
fprintf(fp2,"%c\t\t\t",HT[i].value);fputs(HC[i], fp2); fprintf(fp2, "\n");
}
fclose(fp2);
}
void Encrypt(HuffmanTree HT,HuffmanCode HC)
{
char a[40],c,mood;
FILE* fp2, * fp4;
if ((fp2 = fopen("ToBeTran.txt", "w+")) == NULL) {
printf("无法打开待加密的文件!\n");
}
else {
printf("请输入待加密的内容(以'#'结束):\n");
rewind(stdin);
mood = getchar();
while (mood != '#')
{
fprintf(fp2, "%c", mood);
mood = getchar();
}
rewind(fp2);
fgets(a, 30, fp2);
if ((fp4 = fopen("Result.txt", "w+")) == NULL) printf("无法打开加密结果文件!\n");
else {
for (int i = 0; a[i] != '\0'; i++) {
for (int j = 1; j <= 27; j++)
{
if (a[i] == HT[j].value) fputs(HC[j], fp4);
}
}
rewind(fp2);
printf("待加密文件的内容:\n");
c = fgetc(fp2);
while (c != EOF)
{
putchar(c);
c = fgetc(fp2);
}
}
printf("\n加密的结果:\n");
rewind(fp4);
c = fgetc(fp4);
while (c != EOF)
{
putchar(c);
c = fgetc(fp4);
}
fclose(fp4);
}
fclose(fp2);
}
void Translate(HuffmanTree HT)
{
FILE* fp1,*fp2;
char c;
int i=m;
if ((fp1 = fopen("Result.txt", "r")) == NULL) printf("无法打开待翻译文件!\n");
if ((fp2 = fopen("TextFile.txt", "w+")) == NULL) printf("无法打翻译结果文件!\n");
else
{
printf("待翻译文件的内容:\n");
c = fgetc(fp1);
while (c != EOF)
{
putchar(c);
c = fgetc(fp1);
}
rewind(fp1);
c = fgetc(fp1);
while (c != EOF)
{
if (HT[i].value != '0')
{
fputc(HT[i].value, fp2);
i = m;
}
else
{
if (c == '0') i = HT[i].lchild;
else if (c == '1') i = HT[i].rchild;c = fgetc(fp1);
}
//printf("%d\t", i);
}
fputc(HT[i].value, fp2);
}
printf("\n翻译结果:\n");
rewind(fp2);
c = fgetc(fp2);
while (c != EOF)
{
putchar(c);
c = fgetc(fp2);
}
fclose(fp1);
fclose(fp2);
}
int Interface(){
printf("\t\t\t\n\n\n请选择要进行的操作:\n");
printf("\t\t\t\t\n1.加密文档\n\n2.文档解密\n\n3.退出程序\n");
int ans;
scanf("%d", &ans);
return ans;
}
int main(){
FILE *fp1,*fp3;
HuffmanTree HT;
HuffmanCode HC;
char ciphercode[30];
int w[30];
printf("使用说明:\n\n1.待加密内容由大写字母和空格组成!\
\n2.待加密的内容存放在ToBeTran.txt文件中\
\n3.加密后的内容存放在Result.txt文件中!\
\n4.翻译后的内容存放在TextFile.txt文件中!\n");
if ((fp1 = fopen("FreqData.txt", "r")) == NULL) printf("无法打开频度数据文件!\n");
else if ((fp3 = fopen("CharSet.txt", "r")) == NULL) printf("无法打开字符集文件!\n");
else {
for (int i = 0; i < 27; i++)
{
fscanf(fp1, "%d", &w[i]);
fscanf(fp3, "%C", &ciphercode[i]);
}
HuffmanCoding(HT, HC, w, 27,ciphercode);//for (int i = 0; i < 27; i++) printf("%d\t", w[i]);
do {
int temp = Interface();
switch (temp)
{
case 1:Encrypt(HT, HC); break;
case 2:Translate(HT); break;
case 3:exit(0);
default: printf("该选项不存在!请重新选择:\n");
};
} while (1);
}
return 0;
}
程序正常运行还需要新建几个文本文档:
1.CharSet.txt:存放数据“ ABCDEFGHIJKLMNOPQRSTUVWXYZ”(注意A前面有一个空格!)
2.FreqData.txt:存放数据“ 186 64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1”
3.HfmTree.txt:存放哈夫曼树
4.CodeFile.txt:存放根据频度数据建立的哈夫曼码
5.ToBeTran.txt:存放待加密的内容
6.Result.txt:存放加密的结果
7.TextFile.txt: 存放翻译的结果
第一次发文章,希望能对有需要的同学有帮助。路漫漫其修远兮,也希望各位大佬多多指教