英文文件的压缩和解压缩数据结构与算法课程设计报告.docx
《英文文件的压缩和解压缩数据结构与算法课程设计报告.docx》由会员分享,可在线阅读,更多相关《英文文件的压缩和解压缩数据结构与算法课程设计报告.docx(18页珍藏版)》请在冰豆网上搜索。
![英文文件的压缩和解压缩数据结构与算法课程设计报告.docx](https://file1.bdocx.com/fileroot1/2023-1/21/73e09ef6-ccc6-4ace-97b3-907e884216b4/73e09ef6-ccc6-4ace-97b3-907e884216b41.gif)
英文文件的压缩和解压缩数据结构与算法课程设计报告
合肥学院
计算机科学与技术系
课程设计报告
2009~2010学年第二学期
课程
数据结构与算法
课程设计名称
英文文件的压缩和解压缩
学生姓名
李洋
学号
0804012035
专业班级
08计科
(2)
指导教师
王昆仑
2010年6月7日
题目:
英文文件的压缩和解压缩
1、问题分析和任务定义
采用哈夫曼编码思想实现文件的压缩和解压缩功能,并提供压缩前后的占用空间之比。
要求:
(1)压缩原文件的规模应不小于5K。
(2)提供解压缩后文件与原文件的相同性比较功能,对于一篇文章,首先要打开一篇英文文章,统计该文章中每个字符出现的次数,然后以它们作为权值,对每一个字符进行编码,注意编码表设计及其数据结构,编码完成后再对其编码进行译码。
压缩过程就是以读的方式打开原来的英文文件A.txt,把其中出现的所有字符在文中出项的频率作为权值建立哈弗曼编码。
以写的方式打开一个新的文件,把它作为英文文件压缩后的文本文件,在扫描英文文本文件的所有字符,把其经过一定的转换后存C.txt中。
在扫描C.txt这个文件里的字符把其转换为二进制文件,在把二进制还原为初始的字符。
存入D.txt中。
任务定义是⑴通过独立解决某个课程设计问题,在数据结构的逻辑特性和物理表示、数据结构的选择应用、算法的设计及其实现等方面加深对课程基本内容的理解和综合运用。
⑵深刻理解、牢固掌握数据结构和算法设计技术,提高分析和解决实际问题的能力。
⑶在程序设计方法以及上机操作等基本技能和科学作风方面进行比较系统和严格的训练。
2、数据结构的选择和概要设计
此程序选择的存储结构为顺序存储结构,而使用的哈弗曼树是二叉树的一种特殊结构,可以首先把叶子节点按顺序存放在存储结构中,可以不用任何附加信息就直接寻得其左右孩子节点和父节点,方便建立哈弗曼树。
对于英文文章的压缩和解压缩只要定义生成哈弗曼树和哈弗曼编码的数据类型即可。
即structhead
{
unsignedcharb;
longcount;
longparent,lch,rch;
charbits[256];
}
header[512],tmp;
header【512】中的每个节点包括如下信息:
unsignedcharb
存放的是节点信息
Longcount
每个字符的权值
Longparent
父节点
Longlch
左孩子
Longrch
右孩子
Charbits
每个字符的哈弗曼编码
程序中包含的函数及其功能:
Voidyasuo()
压缩函数,压缩文本文件
Voidjieya()
解压缩文本文件
Voidout()
输出压缩后的编码
Voidmain()
主函数
其中b是存放英文文章中的各个字符,count存放每个字符对应的权值,也就是每个字符在一篇文章中出现的次数,parent,lch,rch是某叶子节点的父节点,和左右孩子节点,bits[]存放各自字符的哈弗曼编码。
tmp则是在生成哈弗曼编码时需要寻找最小权和次小权来作为哈弗曼的树的头两个叶子节点,把它们的和作为根节点,加入到之前的字符编码中去,另外删除最小权和次小权,继续以这种方法生成哈弗曼编码,当到最后一个节点时结束,哈弗曼树生成完毕。
而后就是对文章进行压缩。
Fseek()函数把文件的指针调到ifp待读文件的开始端,与此同时,在把读取的字符经过处理写入ifp文件中,c=fgetc()读取,如果c==header[i].b,即buf中存放的二进制转化为字符与字符中的字符相同,则记录于c.txt中,并把相同的部分给删除。
如此往复,在继续读取由字符的哈弗曼编码所构成的顺序表中的8位,转化为整数,在强制转化为字符型变量,写入c.txt,另外注意的是,当最后的由哈弗曼所构成的二进制文档的字符读取到最后的时候,如果不够8位,的话,则在末尾补零,不然就会出现错误。
while(header[i].bits[0]!
=0)
{
c=0;
for(j=0;j<8;j++)
{
if(header[i].bits[j]=='1')c=(c<<1)|1;
elsec=c<<1;
}
文章压缩成功后,就需要对文档文档进行解压缩,先读取c.txt中的字符文档,是一个一个字符的读取,然后再把读取的一个字符先强制转化为整数,这是根据ASCII码来转化的,最后在将整数转化为二进制编码,是8位的编码,并把这编码存入buf中,在不断读取不断存储,在此过程,用字符中的编码与以读取中的编码进行比较,即if(memcmp(header[i].bits,bx,header[i].count)==0)break;
}if判断语句来判断,如果相等的话,也就是memcmp()函数的值取零,那么就记录这个编码所对应的字符值,即c=header【i】.b,并且写入D.txt中,即fwrite(&c,1,1,ofp);,不断重复这一过程,知道C.txt中的字符都读取完毕,那么存入D.txt的文档也就结束,是源文件一致的英文文件。
3、详细设计和编码
首先,要完成此程序,首先就要建立哈弗曼树,来建立哈弗曼编码,采用顺序表存储结构,
即structhead
{
unsignedcharb;
longcount;
longparent,lch,rch;
charbits[256];
}
header[512],tmp;
首先定义文件指针ifp,ifp=fopen(“A。
txt”),定义flength=0;统计这篇文章的所有的字符的长度,然后在用feof()读取全文信息,用fread()函数一个一个读取字符,header[c].count记录的是每个字符的出现的频率,然后初始化数组,加入header[i].count不为0,即某个字符出现在文中,用ascii码统计header[i].count的数值,如果不为0,那就说明此字符在文中又出现,那么就把它赋值给header[i].b,用强制类型转换。
然后把它的左右孩子和父节点都赋为-1,即为空。
然后寻找最小权和次小权,for(i=n;i然后建树,以最小两个的权值相加作为一个新的节点,在放入之前的各个节点中去,在把之前的那两个最小权给删除掉,继续以这种方式,知道把树建成功为止,在确定哈弗曼编码,从叶子节点开始不断寻找它的父节点,直到寻找到根结点,然后将这些0,1构成一段编码就是哈弗曼编码了。
有了哈弗曼编码就可以实现文件的压缩和解压缩了。
其次,要实现文件的压缩过程,就要从文本中读入一个字符,如果读入的是第i个字符,则把第i个字符的编码写在buf的结尾,buf【0】=0;用于存放所有的字符的哈弗曼编码,即是一连串的二进制编码,当buf的长度大于等于8的时候,把前八位的编码强制转换为整数,之后在把这个整数强制转换为字符,把字符串写入压缩文件,最后把buf中已经转换为整数的那8为编码去除,继续读入哈弗曼编码。
当buf中最后的字符串小于8位时,转换为字符写入压缩文件,在写入压缩文件之后要注意把英文的字符以及其的编码一起写入文件。
最后,要实现的是解压缩过程,解压缩之前必须依靠字符的权值重新进行哈弗曼编码,首先定义一个字符串bx,当bx的长度小于p时,从C.txt中读入一个字符,把字符转换为整数,把整数在转换为二进制码的字符串buf,统计buf中的长度,当小于8位时,补o,然后把字符串buf接着bx末尾。
然后在把bx的部分编码与字符编码比较,若相同,去掉相同的编码,并把程序写入解压后文件。
4、上机调试
问题1:
在文件中一个字符只是占去一个字节,给字符编码后,其编码要如何存入压缩文件才能应道压缩的作用。
解决:
若直接存储,编码只可以用字符串,这样存储空间不仅没有减少反而增大了,所以必须把多个编码存在一个字节中,一字符的形式写入文件。
即:
多个编码->一个八位二进制编码->ascii码->字符(写入到C.txt)
问题2:
生成了压缩文件,可以实现文件的压缩,但不能实现解压.
原因是:
在解压的过程中,吧从压缩文件中读取的字符转化为整数在转化为二进制编码时没有给编码补零。
在开始写程序,一开始在就觉得自己会把各种细节都会考虑到,做了准备,但是在具体实现时却发现真的不是那么回事,是自己没有充分考虑问题。
不知道怎么解压和解压缩。
5、测试结果及其分析
运行程序,进入主菜单选择页面,有四个选项,1。
压缩文档,2。
解压缩文档,3、输出压缩后的文档,4。
退出。
选择第一个时首先输出每个字符以及对应的权值和对应的哈弗曼编码。
而后在输出压缩比,最后生成讲压缩后的文档存放在C.txt中,第二个是实现解压缩功能,将解压缩的文档存放在D.txt中,第三个选项是读取D.txt中的文章,在屏幕中显示。
最后一个选项退出程序。
这是一个主菜单选择界面,确定你要选择的选项,界面清晰明了,1是压缩文档,2是解压缩文档,3是输出解压缩后面的文档,最后是退出。
这个是第一个选项实现的结果,首先输出英文文章的出现的所有的字符,并且输出它们的权值以及哈弗曼编码。
并且将压缩后的文件放入C.txt中
这是运行第二个选项后出现的结果,是解压缩文件后将其存放在D.txt中,其实和源文件是一样的。
输出的是解压缩后的文件。
原英文文件文章截屏如上图所示,也就是需要进行压缩和解压缩的源文件。
压缩后的文件,由于经过处理,就是先将所有的字符的哈弗曼编码顺序存到一个数组buf中,而后每8位提取一下,先转化为整数,然后根据ASCII码表转化为字符,所以出现的是乱码。
解压缩后的文件,与源文件一致,压缩成功。
6、用户使用说明
首先要在和源程序在同一个目录的文件中建立一个英文文本文件A.txt,而后运行程序,第一个是生成一个压缩后的编码,第二个是生成一个解压缩后的编码,也就是和原来的文章一样的文件。
第三个输出解压后文件,最后的是退出程序。
7、参考文献
(1).王昆仑,李红,《数据结构与算法》:
c语言版北京:
中国铁道出版社,2007
(2)何钦铭颜晖《c语言程序设计》北京:
高等教育出版社,2008
8、附录
源程序:
#include"stdio.h"
#include"string.h"
#include"stdlib.h"
structhead{
unsignedcharb;
longcount;
longparent,lch,rch;
charbits[256];}
header[512],tmp;//header[]记录英文文章中共有多少字符
voidyasuo(){
charbuf[512];
unsignedcharc,sum=0;
longi,j,m,n,f;
longmin1,pt1,flength;
FILE*ifp,*ofp;//定义文件指针ifp用于读,ofp用于写
ifp=fopen("A.txt","rb");if(ifp==NULL)
{printf("openerror!
\n");
return;}
ofp=fopen("C.txt","wb");//创建新文件,存放压缩后的英文文章
if(ofp==NULL){
printf("openerror!
\n");return;}
flength=0;//英文文章的长度
while(!
feof(ifp))//文章读取结束,feof返回1,否则返回0
{fread(&c,1,1,ifp);
header[c].count++;//记录每个字符的权值,构建哈弗曼树
flength++;//记录的是一篇文章的长度
}
flength--;header[c].count--;
for(i=0;i<512;i++)//初始化数组
{if(header[i].count!
=0)header[i].b=(unsignedchar)i;//header【i】.b存放字符
elseheader[i].b=0;
header[i].parent=-1;
header[i].lch=header[i].rch=-1;}
for(i=0;i<256;i++)
for(j=i+1;j<256;j++)//选择法排序,header【0】存放的是最权值最大的
{
if(header[i].counttmp=header[i];
header[i]=header[j];
header[j]=tmp;
}}
for(i=0;i<256;i++)
if(header[i].count==0)
break;
n=i;m=2*n-1;//一篇文章中有n个字符,则占取2n-1个空间来存储
for(i=n;i{
min1=999999999;
for(j=0;j
{
if(header[j].parent!
=-1)continue;
if(min1>header[j].count){
pt1=j;
min1=header[j].count;//最小权赋值给min1
continue;
}}
header[i].count=header[pt1].count;//最小权
header[pt1].parent=i;header[i].lch=pt1;min1=999999999;//min1取值这么大是与header【i】.count的值比较,从而找出最小权
for(j=0;j
if(min1>header[j].count){
pt1=j;
min1=header[j].count;
continue;}}//此过程找出的是次小权进行建树
header[i].count+=header[pt1].count;
header[i].rch=pt1;//确定header[i]的左孩子
header[pt1].parent=i;}//i就是开始两个最小的权值的值的和
printf("字符统计对应权值哈弗曼编码\n");
for(i=0;i{printf("%c",header[i].b);//输出的是每个字符的权值
printf("%d\t",header[i].count);//输出每个字符的哈弗曼编码
f=i;
header[i].bits[0]=0;先确定初始情况,
while(header[f].parent!
=-1){//不断去找某个叶子节点的父节点,知道寻找到哈弗曼树的根节点为止
j=f;//记录根节点
f=header[f].parent;
if(header[f].lch==j){
j=strlen(header[i].bits);//记录的是节点i距离根节点的路径长度
memmove(header[i].bits+1,header[i].bits,j+1);//将header[i].bits中移动j+1个字符给header[i].bits+1
header[i].bits[0]='0';}//回到初始情况,继续需找某个叶子节点的父节点,求它的哈弗曼编码
else{//header[i]的右孩子是j的情况
j=strlen(header[i].bits);//求header[i].bits的长度,为了确定哈佛吗编码的长度
memmove(header[i].bits+1,header[i].bits,j+1);//移动j+1个字符从header[i]到header[i]+1的位置
header[i].bits[0]='1';}}//规定某个根节点的右子树编码路径是1
for(f=0;fprintf("%c",header[i].bits[f]);
printf("\n");
}
fseek(ifp,0,SEEK_SET);//压缩过程,将文件指针移到ifp的开始处
fwrite(&flength,sizeof(int),1,ofp);//准备向文件指针ofp所指向的文件也就是D.txt中写入字符
fseek(ofp,8,SEEK_SET);//将文件移动至距离开始8个字符处,因为根据ascii码每个字符的编码时一个整数,可以用二进制的编码来表示
buf[0]=0;
f=0;
pt1=8;
while(!
feof(ifp))//文件读取没有结束,就继续读取,知道结束
{c=fgetc(ifp);f++;//读取字符
for(i=0;i{if(c==header[i].b)break;}//将从D.txt中的读取的字符的编码与字符编码进行比较,如果相等,则用c记录,存储
strcat(buf,header[i].bits);//把前面比较过的二进制编码删除
j=strlen(buf);//求字符串buf中的二进制的长度
c=0;
while(j>=8){//如果buf中字符的长度大于8位时候,就读取8位,处理
for(i=0;i<8;i++){
if(buf[i]=='1')c=(c<<1)|1;
elsec=c<<1;}
fwrite(&c,1,1,ofp);
pt1++;
strcpy(buf,buf+8);//删除之前处理过的8位编码
j=strlen(buf);}
if(f==flength)break;}
if(j>0){
strcat(buf,"00000000");
for(i=0;i<8;i++){
if(buf[i]=='1')c=(c<<1)|1;
elsec=c<<1;}
fwrite(&c,1,1,ofp);
pt1++;}
fseek(ofp,4,SEEK_SET);
fwrite(&pt1,sizeof(long),1,ofp);
fseek(ofp,pt1,SEEK_SET);
fwrite(&n,sizeof(long),1,ofp);
for(i=0;ifwrite(&(header[i].b),1,1,ofp);
c=strlen(header[i].bits);
fwrite(&c,1,1,ofp);
j=strlen(header[i].bits);
if(j%8!
=0)
{
for(f=j%8;f<8;f++)
strcat(header[i].bits,"0");
}
while(header[i].bits[0]!
=0)
{
c=0;
for(j=0;j<8;j++)
{
if(header[i].bits[j]=='1')c=(c<<1)|1;
elsec=c<<1;
}
strcpy(header[i].bits,header[i].bits+8);
fwrite(&c,1,1,ofp);
}}
fclose(ifp);
fclose(ofp);
printf("\n压缩比:
");
printf("%f",(float)4/6);
printf("\n文档压缩成功!
见C.txt\n");
}
voidjieya(){//解压函数
charbuf[255],bx[255];//定义buf,bx存放二进制编码
unsignedcharc;
longi,j,m,n,f,p,l;
longflength;
FILE*ifp,*ofp;//定义文件指针,进行文件的读写操作,其中,ifp用于读取c.txt中的字符,ofp用于向D.txt中写入字符
ifp=fopen("C.txt","rb");//文件处理的具体操作
if(ifp==NULL)
{
printf("sourcefileopenerror!
\n");
return;
}
ofp=fopen("D.txt","wb");
if(ofp==NULL)
{
printf("destinationfileopenerror!
\n");
return;
}
fread(&flength,sizeof(long),1,ifp);
fread(&f,sizeof(long),1,ifp);
fseek(ifp,f,SEEK_SET);
fread(&n,sizeof(long),1,ifp);
for(i=0;i{
fread(&header[i].b,1,1,ifp);
fread(&c,1,1,ifp);
p=(long)c;
header[i].count=p;
header[i].bits[0]=0;
if(p%8>0)
m=p/8+1;
elsem=p/8;
for(j=0;j{
fread(&c,1,1,ifp);
f=c;
itoa(f,buf,2);//二进制把f变成字符型变量,保存在buf中
f=strlen(buf);
for(l=8;l>f;l--){
strcat(header[i].bits,"0");}
strcat(header[i].bits,buf);}
header[i].bits[p]=0;}
for(i=0;ifor(j=i+1;jif(strlen(header[i].bits)>strlen(header[j].bits)){
tmp=header[i];
header[i]=header[j];
header[j]=tmp;}}}
p=strlen(header[n-1].bits);
fseek(ifp,8,SEEK_SET);
m=0;
bx[0]=0;
while
(1){
while(strlen(bx)<(unsignedint)p){
fread(&c,1,1,ifp);
f=c;
itoa(f,buf,2);
f=strlen(buf);
for(l=8;l>f;l--){
strcat(bx,"0");}
strcat(bx,buf);}
for(i=0;iif(memcmp(header[i].bits,bx,header[i].count)==0)break;}
strcpy(bx,bx+header[i].count);
c=header[i].b;
fwrite(&c,1,1,ofp);
m++;
if(m==flength)break;}