用哈夫曼编码实现文件压缩.docx
《用哈夫曼编码实现文件压缩.docx》由会员分享,可在线阅读,更多相关《用哈夫曼编码实现文件压缩.docx(20页珍藏版)》请在冰豆网上搜索。
![用哈夫曼编码实现文件压缩.docx](https://file1.bdocx.com/fileroot1/2023-2/1/98b761ab-8575-460f-8c48-4ebd3250723e/98b761ab-8575-460f-8c48-4ebd3250723e1.gif)
用哈夫曼编码实现文件压缩
《用哈夫曼编码实现文件压缩》
实验报告
课程名称数据结构
实验学期2011至2012学年第2学期
学生所在系部计算机学院
年级2010级专业班级**********
学生姓名******学号************
任课教师######
实验成绩
一、实验题目
哈夫曼编码实现文件压缩
二、实验目的:
1、了解文件的概念。
2、掌握线性链表的插入、删除等算法。
3、掌握Huffman树的概念及构造方法。
4、掌握二叉树的存储结构及遍历算法。
5、利用Huffman树及Huffman编码,掌握实现文件压缩的一般原理。
三、实验设备与环境:
微型计算机、Windows系列操作系统、VisualC++6.0软件。
四、实验内容:
根据ASCII码文件中各ASCII字符出现的频率情况创建Haffman树,再将各字符对应的哈夫曼编码写入文件中,实现文件压缩。
五、概要设计:
本次实验采用将字符用长度尽可能短的二进制数位表示的方法,即对于文件中出现的字符,无须全部都用8位的ASCII码进行存储,根据他们在文件中出现的频率不同,我们利用Haffman算法使每个字符能以最短的二进制字符进行存储,以达到节省存储空间,压缩文件的目的。
解决了压缩需采用的算法,程序的思路已然清晰:
1.统计需压缩文件中每个字符出现的频率。
2.将每个字符的出现频率作为叶子结点构建Haffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列,这样便完成了Haffman编码,将每个字符用最短的二进制字符表示。
3.打开需压缩文件,再将需压缩文件中的每个ASCII码对应的Haffman编码按bit单位输出。
4.文件压缩结束。
六、详细设计:
(1)构造Hufffman树的方法—Hafffman算法
构造Huffman树步骤:
I.根据给定的n个权值{w1,w2,⋯⋯wn},构造n棵只有根结点的二叉树,令起权值为wj。
II.在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和。
III.在森林中删除这两棵树,同时将新得到的二叉树加入森林中。
Ⅳ.重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树。
对于Haffman的创建算法,有以下几点说明:
a)这里的Haffman树采用的是基于数组的带左右儿子结点及父结点下标作为存储结点的二叉树形式,这种空间上的消耗带来了算法实现上的便捷。
b)由于对于最后生成的Haffman树,其所有叶子结点均为从一个内部树扩充出去的,所以,当外部叶子结点数为m个时,内部结点数为m-1,整个Haffman树的需要的结点数为2m-1
c)初始化Hafffman树分两步进行,先将所有结点赋值,再将前m个叶子结点赋初值。
d)在查找权值最小并且父结点为空的两个结点时,通过逐个比较,将两结点的位置下标与权值分别保存。
方便在与其父结点建立联系时调用。
2)压缩过程的实现:
压缩过程的流程是清晰而简单的:
1创建Haffman树→2打开需压缩文件→3将需压缩文件中的每个ASCII码对应的Haffman编码按bit单位输出→4文件压缩结束。
其中,步骤1和步骤3是压缩过程的关键。
a)步骤1:
这里所要做工作是得到Haffman数中各叶子结点字符出现的频率并进行创建。
b)步骤3:
将需压缩文件中的每个ASCII码对应的Haffman编码按bit单位输出,这是本压缩程序中最关键的部分。
这里涉及“转换”和“输出”两个关键步骤:
“转换”部分大可不必去通过遍历Haffman树来找到每个字符对应的哈夫曼编码,可以将每个码值及其对应的ASCII码存放于如下所示的结构体中:
typedefstruct
{
charasciiCode;
unsignedlonghaffCode;
inthaffCodeLen;
}HaffCode;
创建由该结构体结点所组成的,长度为128的一维数组codeList[128],
且codeList中的下标和asciiCode满足下面的顺序存放关系:
codeList[i].asciiCode=i;
这样的话,查找某个字符inChar的Haffman编码的工作便变得相当轻松了,如下:
sHaffCode=codeList[inChar].haffCode;
数组codeList[128]的创建可以采用某种遍历方式下的按找到的字符进行置数的方式,十分的方便。
以下流程图采用的是前序遍历的方式创建:
说明:
1.在流程图中,youBiao中存放字符对应的Haffman编码,sDepth中存放其Haffman编码的长度。
2.在代码的编写过程中,可用递归调用实现。
N
Y
Y
N
Y
N
Y
N
N
Y
(3)“输出”部分是最重要的部分,也是最易出错的部分。
每个字符要能合理的结束。
这主要是为解压缩考虑的,比如在最后,这里涉及到C语言的位操作,要求这个算法能处理好以下几个问题:
1)每个字符所对应的haffCode的比特位长度由5~23位不等长,不可少输,
多输,输错任何一位,后一个字符的haffCode要紧跟在前一个字符的haffCode后面。
2)最后一个要输出的haffCode的最后一位,它恰好是位于最后一个有效字符的第一位,剩下的七个空位是要用无效的haffCode加以填充的。
否则,如果填充的haffCode亦为某个ascii字符的haffCode时,那么在解压缩时,则该在原被压缩文件中不存在的字符便会无中生有的在解压后的文件中出现,这显然是不正确的,应在程序中加以处理。
N
Y
N
Y
Y
N
(4)main函数部分
N
Y
N
Y
七、测试结果及分析:
运行结果:
压缩情况:
实验分析:
利用Huffman树进行编码进行文件的压缩,这一思想在上一学期的离散数学课程里我们有接触到,好在当时学的还不错,所以在构建Huffman树和编码这一过程还是很容易接受的。
困难就在于怎么构造出函数,构造出一个工程,做出一个能够顺利实现压缩的程序。
这是动手能力,实践能力,需要长时间的编程训练作为基础。
数据结构是计算机程序设计的重要理论技术基础,是计算机学科的核心课程之一。
它将各个抽象的数据之间的关系建立起来,无论是线性的、循环的还是分支的,都是为了建立一种方便程序的实现和运行的结构,使得数据之间不再是孤立的。
它能使得我们在编程时在脑海中显现更为清晰的数据关系画面。
而且在学习数据结构时我们更应该联系所属语言(我们所学的是C语言版)的特性,这样才能更好的理解数据结构的思想体系。
总的来说,这次实验带来的收获是很大的,提取文件数据、分析数据、构建Huffman树、替换数据对文件进行压缩、输出文件,一次大的实验几乎运用到了我们一学期所学的所有知识。
经过分、析调试和了解程序的代码,巩固了上课学习的知识。
平时的小程序的编写是训练,而最后的综合实验是作为验收“产品”是否合格的重要依据之一。
所以,我们平时的作业要认真独立完成,否则,在考试和做综合实验时都会很吃力的。
八、教师评语:
教师评价
评定项目
A
B
C
D
评定项目
A
B
C
D
算法正确
界面美观,布局合理
程序结构合理
操作熟练
语法、语义正确
解析完整
实验结果正确
文字流畅
报告规范
题解正确
其他:
评价教师签名:
年月日
附程序:
Code.c
#include"ECBTree.h"
#include"MyAssert.h"
#include
#include
#include
#defineLENGTH128
#defineDEBUG1
#defineREARPOS80
chardotTxt[]=".txt";
chardotRer[]=".rer";
intgetBinLen(unsignedlonginData);
voidmain(intargc,char*argv[])
{
longwList[LENGTH];
unsignedlonghaffCodeList[LENGTH];
inthaffCodeLen[LENGTH];
HaffCodehaffList[LENGTH];
PHtTreemyHtTree;
charinputFileName[LENGTH],outputFileName[LENGTH];
FILE*inputFile,*outputFile,*keyFile;
intfileNameLen;
charinData,outputData;
unsignedlongcurCode,tmpBinData;
intcurLen,realLen,curIndex;
inti;
intcount;
unsignedlongrearCode;/*reardataconsult*/
intrearCodeLen;
if(argc<=1)
{
printf("pleaseenteryourfileaddress.\n");
return;
}
else
{
strcpy(inputFileName,argv[1]);
strcpy(outputFileName,argv[1]);
fileNameLen=strlen(argv[1]);
outputFileName[fileNameLen-4]='\0';
strcat(outputFileName,dotRer);
inputFileName[fileNameLen]='\0';
outputFileName[fileNameLen+4]='\0';
if((inputFile=fopen(inputFileName,"rb"))==NULL)
{
printf("filepathnotfound\n");
return;
}
if(DEBUG)
printf("inputfileopensuccess\n");
assertF((outputFile=fopen(outputFileName,"wb"))!
=NULL,"outputfileerror");
if(DEBUG)
printf("outputfileopensuccess\n");
}
if((keyFile=fopen("KEY.txt","rb"))==NULL)
{
printf(">--keyFilenotfounded--<\n");
return;
}
for(i=0;ifscanf(keyFile,"%d,",&wList[i]);
fscanf(keyFile,"%d;",&wList[i]);
myHtTree=haffmanAlgorithm(LENGTH,wList);
for(i=0;ihaffList[i].asciiCode=(char)i;
preHaffListMake(myHtTree,myHtTree->rootIndex,0x000000,0,haffList);
fprintf(stdout,"haffCodeList:
\r\n");
for(i=0;ifprintf(stdout,"%d,",haffList[i].haffCode);
fprintf(stdout,"%d\r\n",haffList[i].haffCode);
fprintf(stdout,"haffCodeListLen:
\r\n");
for(i=0;ifprintf(stdout,"%d,",haffList[i].haffCodeLen);
fprintf(stdout,"%d\r\n",haffList[i].haffCodeLen);
if(DEBUG)
printf("\nteststart.\n");
curIndex=curLen=0;
rearCode=haffList[REARPOS].haffCode;
rearCodeLen=haffList[REARPOS].haffCodeLen;
while(!
feof(inputFile))
{
count=0;
outputData=0x01;
while(count<8)
{
if(curIndex==curLen)
{
//1.getdata.
if(feof(inputFile))
break;
inData=fgetc(inputFile);
if(inData==-1&&feof(inputFile))
{
if(count==0)
outputData=-1;
else/*therearoutputadjust*/
{
for(i=0;i<8-count;i++)
{
outputData<<=1;
outputData|=((rearCode>>(rearCodeLen-1-i))&0x01);
}
}
/*theconsultbelowwillmakeerrorhappen!
outputData<<=(8-count);
*/
break;
}
curCode=haffList[inData].haffCode;
curLen=haffList[inData].haffCodeLen;
realLen=getBinLen(curCode);
i=curLen-realLen;
curIndex=0;
}
if(i>0)
{
outputData<<=1;
//noneedtofillbitdata.
i--;
}
else
{
tmpBinData=(curCode>>(curLen-curIndex-1))&0x01;
outputData<<=1;
outputData|=(char)tmpBinData;
}
/*-----------------------------------*/
curIndex++;
count++;
}
fputc(outputData,outputFile);
}
if(DEBUG)
printf("\ntestends.\n");
fclose(inputFile);
fclose(outputFile);
getchar();
return;
}
intgetBinLen(unsignedlonginData)
{
inti=0;
if(inData==0)
return1;
else
while(inData!
=0)
{
inData/=2;
i++;
}
returni;
}
ECBTree.c
#include"ECBTree.h"
#include"MyAssert.h"
#include
#include
PHtTreehaffmanAlgorithm(intm,EBTreeType*w)
{
PHtTreepht;
inti,j;
intfirstMinIndex,secondMinIndex;
intfirstMinW,secondMinW;
pht=(PHtTree)malloc(sizeof(structHtTree));
assertF(pht!
=NULL,"inhaffmanalgorithm,memapplyfailure\n");
/*Initializethetreearray*/
for(i=0;i<2*m-1;i++)
{
pht->ht[i].llinkIndex=-1;
pht->ht[i].rlinkIndex=-1;
pht->ht[i].parentIndex=-1;
if(i{
pht->ht[i].ww=w[i];
pht->ht[i].info=(char)i;
}
else
pht->ht[i].ww=-1;
}
for(i=0;im-1
{
firstMinW=MAXCHAR;
firstMinIndex=-1;
secondMinW=MAXCHAR;
secondMinIndex=-1;
for(j=0;j{
if(pht->ht[j].wwht[j].parentIndex==-1)
{
//transminFirstinfotominSecondinfo
secondMinIndex=firstMinIndex;
secondMinW=firstMinW;
//setnewfirstminnode.
firstMinIndex=j;
firstMinW=pht->ht[j].ww;
}
elseif(pht->ht[j].wwht[j].parentIndex==-1)
{
secondMinW=pht->ht[j].ww;
secondMinIndex=j;
}
}
//Constructanewnode.m+iiscurrentnewnode'sindex
pht->ht[firstMinIndex].parentIndex=m+i;
pht->ht[secondMinIndex].parentIndex=m+i;
pht->ht[m+i].ww=firstMinW+secondMinW;
pht->ht[m+i].llinkIndex=firstMinIndex;
pht->ht[m+i].rlinkIndex=secondMinIndex;
pht->rootIndex=m+i;
}
returnpht;
}
/*Invoke:
preHaffListMake(myHtTree,myHtTree->rootIndex,0x00,0,myList)
*/
voidpreHaffListMake(PHtTreeinTree,introotIndex,unsignedlongyouBiao,intsDepth,HaffCode*inList)
{
if(inTree->ht[rootIndex].llinkIndex==-1&&inTree->ht[rootIndex].rlinkIndex==-1)
{
inList[inTree->ht[rootIndex].info].haffCode=youBiao;
inList[inTree->ht[rootIndex].info].haffCodeLen=sDepth;
}
else
{
preHaffListMake(inTree,inTree->ht[rootIndex].llinkIndex,youBiao<<1,sDepth+1,inList);
preHaffListMake(inTree,inTree->ht[rootIndex].rlinkIndex,(youBiao<<1)|0x01,sDepth+1,inList);
}
}
MyAssert.c
#include"myAssert.h"
#include
#include
voidassertF(intcondition,char*errorMsg)
{
if(!
condition)
{
printf("\n%s\n",errorMsg);
abort();
}
}
ECBTree.h
#ifndefECBTREE_H
#defineECBTREE_H
#defineEBTreeTypelong
#defineMAXCHAR30000
#defineMAXNODE300
structHtNode
{
EBTreeTypeww;
charinfo;
intparentIndex;
intllinkIndex;
intrlinkIndex;
};
structHtTree
{
structHtNodeht[MAXNODE];
introotIndex;
};
typedefstructHtTree*PHtTree;
typedefstruct
{
charasciiCode;
unsignedlonghaffCode;
inthaffCodeLen;
}HaffCode;
externPHtTreehaffmanAlgorithm(intm,EBTreeType*w);
externvoidpreHaffListMake(PHtTreeinTree,introotIndex,unsignedlongyouBiao,intsDepth,HaffCode*inList);
#endif
Global.h
#ifndefGLOBAL_H
#defineGLOBAL_H
#defineTypelong
#endif
MyAssert.h
/*myAssert.h*/
#ifndefMYASSERT_H
#defineM