《哈夫曼编译码器》实验报告(共35页).doc

上传人:飞****2 文档编号:8576875 上传时间:2022-03-19 格式:DOC 页数:35 大小:684KB
返回 下载 相关 举报
《哈夫曼编译码器》实验报告(共35页).doc_第1页
第1页 / 共35页
《哈夫曼编译码器》实验报告(共35页).doc_第2页
第2页 / 共35页
点击查看更多>>
资源描述

《《哈夫曼编译码器》实验报告(共35页).doc》由会员分享,可在线阅读,更多相关《《哈夫曼编译码器》实验报告(共35页).doc(35页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、精选优质文档-倾情为你奉上哈夫曼编/译码器作者:田正英 联系方式: 目录 一、实验目的 二、实验内容 三、语言 四、环境 五、功能设计概述 六、详细设计 七、测试结果 八、收获与心得 九、程序代码哈夫曼编/译码器1、 实验目的1. 熟练掌握哈夫曼编译原理2. 掌握程序设计步骤二、实验内容根据哈夫曼编码原理,设计一个程序,在已知相关字符和字符对应权值(文件中存在或者用户输入)的情况下,根据用户要求对相应内容进行编码、译码等相应操作。3、 语言C语言四、编译环境Microsoft Visual C+5、 功能设计概述1. 从标准输入端输入相关的字符和权值2. 根据相关字符和权值构造哈夫曼树,并将该

2、树的相关信息写入指定文件3. 若编译码前未对哈夫曼树进行初始化,则默认为指定文件中已纯在该哈夫曼树,并将其读入到内存(因此未初始化前一定要确定指定文件中存在哈夫曼树,否则会将空树读入内存)4. 将编码结果写入指定文件 5. 将指定文件中编码内容进行哈夫曼译码6. 标准端输出已保存到相关文件中的信息(哈夫曼树存储内容、代码、译码后内容)7. 输出哈夫曼树直观图并保存到指定文件8. 用户选择界面6、 详细设计typedef struct Htnodeint lchild,rchild,parent;float weight;char data; Htnode;1. 哈夫曼树结构体:分别存储该节点左

3、右孩子、双亲节点、该节点权值、以及该节点对应字符。typedef struct Hcodechar cdsize;int start;Hcode;分别存储相应字符对应代码以及代码开始位置。此处 size 宏定义为 128 。2. 输入Inputgetchar();printf(请输入初始化字符和权值,输入#结束n);do/ 输入字符及权值 (*n)+;scanf(%c, &t*n.data);/ 注意 空格也要算字符,也要输入权值 if (t*n.data != #) scanf(%f, &t*n.weight);getchar(); while (t*n.data != #); 用户根据相关

4、提示输入需要字符和权值(输入#结束)。函数中两处用到getchar() 函数,第一处是为吸收从调用方可能引进的回车,第二处是为吸收每次输入完成后的回车。第一处可视情况添加(为保证健壮性,最好添加),第二处必须添加。3. 初始化相关函数 CreatHt、CreatHcode、Initializationfor (int j = n; j2 * n - 1; j+)float min1 = 32000;float min2 = 32000;int node1 = -1;int node2 = -1;for (int k = 0; kj; k+)if (bk.parent = -1)if (bk.w

5、eight min1)min2 = min1;node2 = node1;min1 = bk.weight;node1 = k;else if (bk.weight min2)min2 = bk.weight;node2 = k;bj.lchild = node1;bj.rchild = node2;bnode1.parent = bnode2.parent = j;bj.weight = min1 + min2;以上为CreatHt函数部分代码。找出叶子节点中(0 n-1)最小的一个和第二小的一个节点, 由这两个节点生成双亲节点(n 2*n-1),双亲节点伪权值为该两节点权值(或伪权值)之和

6、。重复以上操作直到所有节点(2*n-1个)都确定了父子关系。Hcode ch;for (int i = 0; in; i+)ch.start = n;int f = bi.parent;int c = i;while (f != -1)if (bf.rchild = c)ch.cdch.start- = 1;else ch.cdch.start- = 0;c = f;f = bf.parent;ch.start+;cdi = ch;以上为CreatHcode函数部分代码。从叶子节点(存储字符的节点)倒序找双亲节点,判断是双亲左孩子还是右孩子,若是左孩子则在字符代码数组最后一位填0( start

7、标记前移一位),若是右孩子则在字符代码数组最后一位填1( start标记前移一位)。重复以上操作直到找到根结点。注意start标记的处理,此处一错满盘皆输,而且难发现错误原因。FILE *fp = fopen(op, wb+); for (int i = 0; i2 * n - 1; i+)if (in)fprintf(fp, %ct, ti.data);else fprintf(fp, #t, ti.data);fprintf(fp, %.2ft, ti.weight);fprintf(fp, %dt, ti.parent);fprintf(fp, %dt, ti.lchild);fprin

8、tf(fp, %d, ti.rchild);fprintf(fp, rn);以上是Initialization函数部分代码。将所有节点信息及节点间对应关系保存到指定文件。下次编译时则无需以上繁琐的初始化过程,只需从文件中读取即可。4. 编码Encodingint j = 0;while (!feof(bb)strj = fgetc(bb);j+;int rt = 0;for (int i = 0; ij; i+) /对 str 中每个字符进行编译 for (int k = 0; kn; k+) / 在原码中查找该字符 if (pk.data = stri)for (int a = dk.sta

9、rt; a = n; a+)codert+ = dk.cda;以上为Encoding函数部分代码。将源文件字符读到自定义缓冲区str中,将str中每个字符在初始化字符中查找,若找到则将该字符对应代码写到 code 中。其中j起到计数的作用,记录源文件字符个数。5. 译码Decodingint cs = 0;ip = 0;flag1 = 1;while (ip != len)flag1 = 1;for (int j = 0; jn & flag1; j+)/遍历每一个字符,直到找到为止 int flag2 = 1;int i = dj.start;for (; i = n & flag2; i+

10、)if (dj.cdi != codip + i - dj.start)flag2 = 0;if (flag2 = 1)ip = ip + i - dj.start;strcs+ = pj.data;flag1 = 0;以上为Decoding函数部分代码。将初始化字符中每一个字符代码和要编译内容相应位置代码进行比对 ,若找到符合的,则相对应字符写入字符数组str中。其中ip 起计数作用,记录共编译的代码长度。ip = ip + i - dj.start;将每个已找到字符代码长度累加,ip = len时译码完成。6. 文件中哈夫曼树读入到内存 ReadHtFile*n = 0;while (!f

11、eof(fp) fscanf(fp, %c, &ti.data);if (ti.data != #) (*n)+;fgetc(fp);fscanf(fp, %f, &ti.weight);fgetc(fp);fscanf(fp, %d, &ti.parent);fgetc(fp);fscanf(fp, %d, &ti.lchild);fgetc(fp);fscanf(fp, %d, &ti.rchild);fgetc(fp);i+;(*n)- -;将指定文件中的哈夫曼树写入到内存 ,fgetc(fp)是为读取每个数据之间制表符或者回车符,此处要格外小心,极易出错,稍偏差一位后面就全部错位。此处

12、是以文本文件方式读取(若以二进制方式读取应注意回车符处理)。注意:若用二进制方式读文件则行末需读两个字符 7. 标准端输出Output、OutputHt、OutputCode、OutputFile、OutputTfor (int i = 0; in; i+) int len = n - codi.start + 1;int h = len + 1;printf(%c , ati.data);printf(%.2f t, ati.weight);for (int j = 0; jh; j+)printf();printf(n);for (int k = n; k2 * n - 1; k+)int

13、 len = 0;int f = k;while (atf.parent != -1)len+;f = atf.parent;int h = len + 1;printf(%c , atk.data);printf(%.2f t, atk.weight);for (int j = 0; jh; j+)printf();printf(n);以上为Output函数部分代码。将哈夫曼树直观图(凹入式)输出到标准端,第一个for循环输出叶子节点,第二个for循环输出非叶子节点 。叶子节点直接根据代码长度输出,该叶子节点深度等于代码长度加1,而非叶子节点则需逆向找双亲(与叶子节点求代码过程类似),直到找

14、到根结点,将到根结点经过的边数加1即其深度。fgets(str, maxsize - 1, pp);while (!feof(pp)printf(%s, str);fgets(str, maxsize - 1, pp);以上为OutputHt函数的部分代码。将哈夫曼树存储内容输出到标准端(字符、双亲、孩子、权值)。fgets(str, maxsize - 1, cdd);while (!feof(cdd)printf(%s, str);fgets(str, maxsize - 1, cdd); 以上为OutputCode函数部分代码。将编码后的代码内容输出到标准端。fgets(str, max

15、size - 1, ott);while (!feof(ott)fgets(str, maxsize - 1, ott);printf(%s, str);以上为OutputFile函数部分代码。将译码后的内容输出到标准端。for (int i = 0; in; i+)/ 输入叶子节点 int len = n - codi.start + 1;int h = len + 1;fprintf(ote, %c , ati.data);fprintf(ote, %.2f t, ati.weight);for (int j = 0; jh; j+)fprintf(ote, );fprintf(ote,

16、n);for (int k = n; k2 * n - 1; k+)/输入非叶子节点 int len = 0,f = k;while (atf.parent != -1)len+;f = atf.parent;int h = len + 1;fprintf(ote, %c , atk.data);fprintf(ote, %.2f t, atk.weight);for (int j = 0; jh; j+)fprintf(ote, );fprintf(ote, n);以上为OutputT函数部分代码。将哈夫曼树直观图(凹入式)输出到指定文件。此函数与 Output函数相似,此处不再讲述。8.

17、用户选择界面mainswitch (flag)case 1: /初始化并保存printf(初始化将会删除原文件中的哈夫曼树,确认初始化吗?是(1)否(2):);scanf(%d, &yn);switch (yn)getchar();case 1: /确认初始化Input(op, t, co, &nn); CreatHt(t, nn);/构造哈夫曼树 CreatHcode(t, co, nn);Initialization(t, op, nn);/ 将哈夫曼树写入到指定文件tag = 1;break;case 2: break;default:printf(选择有误n);break;case 2

18、:if (!tag)ReadHtFile(op, t, co, &nn);CreatHt(t, nn);CreatHcode(t, co, nn);tag = 1;Encoding(be, cod, t, co, nn);break;case 3: if (!tag)ReadHtFile(op, t, co, &nn);CreatHt(t, nn);CreatHcode(t, co, nn);tag = 1;Decoding(cod, out, t, co, nn);break; case 4: /显示哈夫曼树存储内容case 5: /显示代码case 6: /显示译码后内容case 7: /

19、显示哈夫曼树直观图并保存if (!tag)/若内存中不存在哈夫曼树,则需要从文件中读取,并在内存中构造哈夫曼树 ReadHtFile(op, t, co, &nn);CreatHt(t, nn);/构造哈夫曼树 CreatHcode(t, co, nn);tag = 1;Output(op, be, cod, out, outtree, flag, t, co, nn);/输出哈夫曼树直观图,并保存到指定文件 break;case Q: /退出break;default:printf(输入的选择有误,请重新输入n);/输入错误以上为main函数部分代码。用户根据菜单及相应提示进行需要的操作,每

20、执行完一次操作重新输出菜单等待用户下一个操作(错误输入会有相关提示),初始化会使文件中哈夫曼树被删除,因此也会有相关提醒,直到用户输入Q退出程序。7、 测试结果1. 查看菜单 2. 初始化3. 编码 4. 译码5. 显示代码6.显示译码后内容7. 显示哈夫曼树存储内容8. 显示哈夫曼树直观图9. Q退出10. 查看文件ToBeTran.txt (源文件)Codefile.txt(代码文件)TextFile.txt(译码文件)hfmTree.txt(哈夫曼树存储内容文件)TreePrint.txt(哈夫曼树直观图文件)8、 收获与心得本人历经3天才完成这个实验项目,开始两天写代码,隔了一段时间又

21、花了一天写实验报告,收获颇多。这是我第一次写这么长代码的一个程序(算了一下大概600行),平时也一直写代码但都是小程序(200左右)。而且我是非常认真地做这个实验,我觉得应该自己独立完成,不要上网查别人的代码,更不能抄,这样才能有所提高。对于写这个程序,一开始我是从小的方面入手,先把输入的函数完成,调试无误后再写初始化函数,经过多次输入并调试,在文件中查看,确认无误后再写下一个函数,待所有函数写完再来设计用户选择界面,做这一步操作时需该动一些函数的结构,比如输出函数Output,需要引入主函数的标志flag。程序完工后,再进行整体调试,尽可能找到最多的bug,这个过程往往是最耗时的,比如函数过

22、多很难定位是哪里出现问题。测试数据我也做了好几次改的,开始是一句话,挺好,成功了,改成多行就出错了,其中就包含回车符的处理。然后改成一大篇文章,又出现一些bug,比如数组不够大等问题,这些问题耽误了我好长一段时间,后来经过改动,比如把编码数组和译码数组移到函数外,使之变成全局变量,还不能掉了static。写的过程是艰辛的但每写完一个函数都会有成就感,当看到自己写的程序正常的跑起来的时候,整个人都是高兴的。而且还能收获很多知识,更加熟练地掌握了哈夫曼编码的原理和算法设计。以前都不知道数该用在何处,写完这个程序看到了所学的树形结构的应用。 深刻掌握了文件操作的方法和细节(克服了文本文件和二进制文件

23、读取极易出错的地方)。总之是自己独立完成的,一定会有收获。九、程序代码1. 头文件“Hf.h”#define _CRT_SECURE_NO_WARNINGS#include#include#define size 128 /存储字符相应代码#define maxsize 1024*10 / 存储需要编码的内容#define maxlen 1024*100 / 存储译码后代码typedef struct Htnodeint lchild;int rchild;int parent;float weight;char data; Htnode;typedef struct Hcodechar cd

24、size;/ 下标从 start 到 n , n 也算 int start;Hcode;void Input(char *op, Htnode *t, Hcode *co, int *n);void ReadHtFile(char *op, Htnode *t, Hcode *co, int *n);void Initialization(Htnode *t, char *op, int n);void CreatHt(Htnode *b, int n);void CreatHcode(Htnode *b, Hcode *cd, int n);void Encoding(char *b, ch

25、ar *t, Htnode *p, Hcode *d, int n);void Decoding(char *t, char *end, Htnode *p, Hcode *d, int n);void Output(char *p, char *b, char *cd, char *ot, char *ott, char ip, Htnode *at, Hcode *cod, int n);void OutputT(char *outtree, Htnode *at, Hcode *cod, int n);void OutputFile(char *ot);void OutputCode(c

26、har *cd);void OutputHt(char *p);2. 源文件“HFmain.cpp”/ 功能:哈夫曼编/译码器 / 作者:田正英/时间:2016/12/1#includehf.hint main()/功能:用户选择界面Htnode tsize;/存放每个字符相关信息(双亲、孩子、数据、权值) Hcode cosize;/存放每个字符的代码 及起始读码位置 char *op = E:hfmTree.txt;/存放哈夫曼树存储内容路径 char *be = E:ToBeTran.txt;/需要译码文件路径 char *cod = E:Codefile.txt;/写入代码的文件路径

27、char *out = E:TextFile.txt;/译码结果写入的文件路径 char *outtree = E:TreePrint.txt;/ 将哈夫曼直观图写入指定文件 int nn = 0;char flag = 0;/程序功能选择 int tag = 0;/判断内存中是否存在哈夫曼树 ,1 存在 , 0 不存在 while (flag != Q)printf(* * * * * * * * * * * * * * * * * *n);/ 用户选择界面 printf(* * 1.初始化并保存tt* *n* * 2.编码并保存tt* *n* * 3.译码并保存tt* *n* * 4.显示

28、哈夫曼树存储内容t* *n);printf(* * 5.显示代码ttt* *n* * 6.显示译码后内容tt* *n* * 7.显示哈夫曼树直观图并保存t* *n* * Q.退出ttt* *n);printf(* * * * * * * * * * * * * * * * * *n请选择需要执行的内容:);scanf( %c, &flag);getchar();int yn = 0;switch (flag)case 1: /初始化并保存printf(初始化将会删除原文件中的哈夫曼树,确认初始化吗?是(1)否(2):);/危险操作再次确认 scanf(%d, &yn);switch (yn)g

29、etchar();case 1: /确认初始化Input(op, t, co, &nn); /输入字符及权值, 并将字符代码初始化为 0 以保健壮 CreatHt(t, nn);/构造哈夫曼树 CreatHcode(t, co, nn);Initialization(t, op, nn);/ 将哈夫曼树写入到指定文件tag = 1;break;case 2: /不初始化break;default:printf(选择有误n);break;case 2: /编码并保存if (!tag)/若内存中不存在哈夫曼树,则需要从文件中读取,并在内存中构造哈夫曼树 ReadHtFile(op, t, co,

30、&nn);CreatHt(t, nn);/构造哈夫曼树 CreatHcode(t, co, nn);tag = 1;Encoding(be, cod, t, co, nn);/对文件中内容进行编码,并将编码内容写入指定文件 break;case 3: /译码并保存if (!tag)/若内存中不存在哈夫曼树,则需要从文件中读取,并在内存中构造哈夫曼树 ReadHtFile(op, t, co, &nn);CreatHt(t, nn);/构造哈夫曼树 CreatHcode(t, co, nn);tag = 1;Decoding(cod, out, t, co, nn);/将已编码完成的内容进行 译

31、码 并写入到指定文件 break;case 4: /显示哈夫曼树存储内容case 5: /显示代码case 6: /显示译码后内容case 7: /显示哈夫曼树直观图并保存if (!tag)/若内存中不存在哈夫曼树,则需要从文件中读取,并在内存中构造哈夫曼树 ReadHtFile(op, t, co, &nn);CreatHt(t, nn);/构造哈夫曼树 CreatHcode(t, co, nn);tag = 1;Output(op, be, cod, out, outtree, flag, t, co, nn);/输出哈夫曼树直观图,并保存到指定文件 break;case Q: /退出br

32、eak;default:printf(输入的选择有误,请重新输入n);/输入错误printf(n_n);/执行完毕,准备执行下个任务 printf(_n);return 0;3. 源文件“Initialization.cpp”/功能:根据相关字符和权值构造哈夫曼树,并将该树的相关信息写入指定文件#includehf.hvoid CreatHt(Htnode *b, int n) /构造哈夫曼树结构关系 for (int i = 0; i2 * n - 1; i+)bi.lchild = bi.rchild = bi.parent = -1;for (int j = n; j2 * n - 1;

33、 j+)float min1 = 32000;float min2 = 32000;int node1 = -1;int node2 = -1;for (int k = 0; kj; k+)if (bk.parent = -1)if (bk.weight min1)min2 = min1;node2 = node1;min1 = bk.weight;node1 = k;else if (bk.weight min2)min2 = bk.weight;node2 = k;bj.lchild = node1;bj.rchild = node2;bnode1.parent = bnode2.pare

34、nt = j;bj.weight = min1 + min2;for (int j = n; j2 * n - 1; j+)/ 将所有非叶子节点数据域赋 # 以便观察 bj.data = #;void CreatHcode(Htnode *b, Hcode *cd, int n)/ 求哈夫曼字符代码 Hcode ch;for (int i = 0; in; i+)ch.start = n;int f = bi.parent;int c = i;while (f != -1)if (bf.rchild = c)ch.cdch.start- = 1;else ch.cdch.start- = 0;

35、c = f;f = bf.parent;ch.start+;cdi = ch;void Initialization(Htnode *t, char *op, int n) /将哈夫曼树中各节点信息写入指定文件 FILE *fp = fopen(op, wb+);/ 打开要写入哈夫曼树的文件 if (!fp)printf(cannot open hfmTree.txtn);printf(初始化失败n);return;for (int i = 0; i2 * n - 1; i+) / 将哈夫曼树写入到指定文件 if (in)fprintf(fp, %ct, ti.data);else fprin

36、tf(fp, #t, ti.data);fprintf(fp, %.2ft, ti.weight);fprintf(fp, %dt, ti.parent);fprintf(fp, %dt, ti.lchild);fprintf(fp, %d, ti.rchild);fprintf(fp, rn);fclose(fp);printf(初始化完成n);4. 源文件“Input.cpp”/功能:从标准输入端输入相关的字符和权值 #includehf.hvoid Input(char *op, Htnode *t, Hcode *co, int *n) /从标准输入端输入哈夫曼树 字符和权值 (*n) = -1;getchar();printf(请输入初始化字符和权值,输入#结束n);do/ 输入字符及权值 (*n)+;scanf(%c, &t*n.data);/ 注意 空格也要算字符,也要输入权值 if (t*n.data != #) scanf(%f, &t*n.weight);getchar(); while (t*n.data != #);for (int j = 0; j = *n; j+)/字符代码初始化 memset(coj.cd, 0, *n + 1);5. 源文件“Decoding.cpp”/功能:将指定文件中编码内容进行哈夫曼译码#includehf.hstatic

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 应用文书 > 教育教学

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com