数据结构实验报告
实验名称:文件压缩 实验类型:综合性试验 班级:20112111 学号:2011211107 姓名:冯若航
实验日期:2003.6.19 下午4:00
1.问题描述
文件压缩 ①基本要求
哈夫曼编码是一种常用的数据压缩技术,对数据文件进行哈夫曼编码可大大缩短文件的传输长度,提高信道利用率及传输效率。要求采用哈夫曼编码原理,统计文本文件中字符出现的词频,以词频作为权值,对文件进行哈夫曼编码以达到压缩文件的目的,再用哈夫曼编码进行译码解压缩。
统计待压缩的文本文件中各字符的词频,以词频为权值建立哈夫曼树,
并将该哈夫曼树保存到文件HufTree.dat中。
根据哈夫曼树(保存在HufTree.dat中)对每个字符进行哈夫曼编码,并
将字符编码保存到HufCode.txt文件中。
压缩:根据哈夫曼编码,将源文件进行编码得到压缩文件CodeFile.dat。 解压:将CodeFile.dat文件利用哈夫曼树译码解压,恢复为源文件。 2.数据结构设计
此类问题,应设计文件的数据结构。
* * * * * *
赫夫曼树节点的数据结构 typedef struct node {
long w;
//权
//父亲,左孩子,右孩子 //霍夫曼树的结点
short p,l,r; 4 1 4
压缩头标记 文件名长度 文件名 源文件长度 huffman树 文件内容
ns 1020
1021~EOF
}HTNODE,*HTNP;
.
精品文档
赫夫曼编码数组元素的数据结构设计 typedef struct huffman_code {
BYTE len;
//长度 //字符串
BYTE *codestr;
}HFCODE; //霍夫曼编码数组元素
3.算法设计 源代码
#define _CRT_SECURE_NO_DEPRECATE #include typedef unsigned int UINT; typedef unsigned char BYTE; typedef struct node { long w; //权 //父亲,左孩子,右孩子 //霍夫曼树的结点 short p,l,r; }HTNODE,*HTNP; { BYTE len; typedef struct huffman_code //长度 //字符串 BYTE *codestr; }HFCODE; //霍夫曼编码数组元素 #define OK 1 #define ERROR -1 #define UNUSE -1 //未链接节点标志 #define CHAR_BITS 8 #define INT_BITS 32 #define BUFFERSIZE 256 //一个字符中的位数 //一个整型中的位数 //霍夫曼编码个数 //缓冲区大小大小 #define HUFCODE_SIZE 256 #define UINTSIZE sizeof(UINT) #define BYTESIZE sizeof(BYTE) #define TAG_ZIGHEAD 0xFFFFFFFF #define MAX_FILENAME 512 //函数声明 //压缩模块 int Compress(char *SourceFilename,char *DestinationFilename); //压缩调用 //压缩文件头标 int Initializing(char *SourceFilename,FILE **inp,char *DestinationFilename,FILE **outp); //初始化文件工作环境 long AnalysisFiles(FILE *in,long frequency[]); . 精品文档 //计算每个不同字节的频率以及所有的字节数 //生成霍夫曼树 //霍夫曼编码 //查找当前最小权值的霍夫曼树节点并置为占用 //将一个字符数组转换成二进制数字 //查找当前最小权值的霍夫曼树节点并置为占用 int CreateHuffmanTree(long w[],int n,HTNODE ht[]); int HuffmanTreeCoding(HTNP htp,int n,HFCODE hc[]); int Search(HTNP ht,int n); BYTE Char2Bit(const BYTE chars[CHAR_BITS]); int Search(HTNP ht,int n); int WriteZipFiles(FILE *in,FILE *out,HTNP ht,HFCODE hc[],char* SourceFilename,long source_filesize);//写压缩文件 //解压缩模块 int DeCompress(char *SourceFilename,char *DestinationFilename); //解压缩调用 int Initializing_Dezip(char *SourceFilename,FILE **inp,char *DestinationFilename,FILE **outp); //为处理解压缩流程初始化文件 void ReadHuffmanTree(FILE* in,short mini_ht[][2]); //从待解压缩文件中读取huffman树 //写解压缩文件 //报错 int WriteDeZipFiles(FILE *in,FILE* out,short mini_ht[][2],long bits_pos,long Dst_Filesize); //函数定义 //函数实现 //压缩 int Compress(char *SourceFilename,char *DestinationFilename) { //处理各个字符的频率,并输出原始文件长度 //处理待压缩与压缩文件文件 Initializing(SourceFilename,&in,DestinationFilename,&out); puts(\"Loading Files...\"); FILE *in,*out; int i; //输入输出流 //计数变量 void ErrorReport(int error_code); float Compress_rate; //存放压缩率 //存放256个字符的huffman编码 HFCODE hc[HUFCODE_SIZE]; HTNODE ht[HUFCODE_SIZE*2-1]; //256个字符的huffman树需要2*256-1=511个节点。 long frequency[HUFCODE_SIZE ],source_filesize,Dst_Filesize=0;//字符频率数组,源文件, 目标文件。 . 精品文档 source_filesize=AnalysisFiles(in,frequency); puts(\"Loading Complete!Now Analysising...\"); printf(\"Original size:%ld Byte \\n\",source_filesize); //创建Huffman树 CreateHuffmanTree(frequency,HUFCODE_SIZE,ht); //Huffman编码 puts(\"Huffman Tree Initialized Successfully,HuffmanCoding Processing...\"); HuffmanTreeCoding(ht,HUFCODE_SIZE,hc); //计算目标文件长度 for(i=0;i Dst_Filesize+=frequency[i]*hc[i].len; }//将文件主体部分的位个数转换为字节个数; Dst_Filesize=Dst_Filesize%8==0?Dst_Filesize/8:Dst_Filesize/8+1; for(i=0;i //源文件名占用空间 //源文件名长度信息占 Dst_Filesize+=2*sizeof(short); 用空间 //文件头长度 //压缩率 Compress_rate=(float)Dst_Filesize/source_filesize; puts(\"Coding Successfully.Now producing zip files...\"); printf(\"Compressed File Size:%ld Byte,radiation: %%%.2lf //生成压缩文件 WriteZipFiles(in,out,ht,hc,SourceFilename,source_filesize); puts(\"Compress Complete!\"); //擦屁股 fclose(in); fclose(out);//关闭文件 for(i=0;i free(hc[i].codestr); \\n\",Dst_Filesize,Compress_rate*100); . 精品文档 } int Initializing(char *SourceFilename,FILE **inp,char *DestinationFilename,FILE **outp) { } long AnalysisFiles(FILE *in,long frequency[]) { 加 for(i=0;i for(i=0,filesize=0;i //计算文件长度,计算方法是把所有字符的频数相 read_len=fread(buf,1,BUFFERSIZE,in); for(i=0;i //统计字频 //将文件定位符指向文件开头 //设置读入长度=缓冲区长度 //每当读取字符长度达到缓冲区长度时 frequency[i]=0; //初始化所有字符频率为0 int i,read_len; BYTE buf[BUFFERSIZE]; long filesize; //缓冲区 //文件总长 return OK; if((*outp=fopen(DestinationFilename,\"wb\"))==NULL) {//文件打开错误 } if((*inp=fopen(SourceFilename,\"rb\"))==NULL) {//文件打开错误 } return ERROR; return ERROR; if(strcmp(SourceFilename,DestinationFilename)==0) {//重名判断 } printf(\"Source File:%s,Destination File:%s\\n\",SourceFilename,DestinationFilename); return ERROR; while(read_len==BUFFERSIZE) . 精品文档 } } return filesize; int Search(HTNP ht,int n) { } int CreateHuffmanTree(long w[],int n,HTNODE ht[]) { int m,i,s1,s2; if (n<1) return ERROR; m=2*n-1; //霍夫曼树节点总数=叶子数*2-1 if (ht==NULL) return ERROR; for(i=0;i ht[i].w=ht[i].p=ht[i].l=ht[i].r=UNUSE; //初始化分支节点 ht[i].w=w[i],ht[i].p=ht[i].l=ht[i].r=UNUSE; //初始化叶子节点 int i,x; for(x=0;x //将叶子节点占用 if(ht[i].p==UNUSE&&ht[i].w //找权值最小的叶子节点 if(ht[x].p==UNUSE) break; //找到第一个没有使用的叶子节点,跳出 用完为止 s1=Search(ht,i); s2=Search(ht,i); //找到权值最小的两个节点,这里通过//设置父节点 //设置孩子 //设置权为两个孩子权之和 两次调用寻找最小权值的函数search解决问题 ht[s1].p=ht[s2].p=i; ht[i].l=s1,ht[i].r=s2; ht[i].w=ht[s1].w+ht[s2].w; . 精品文档 } return OK; int HuffmanTreeCoding(HTNP htp,int n,HFCODE hc[]) { 量 } BYTE Char2Bit(const BYTE chars[CHAR_BITS]) { } int WriteZipFiles(FILE *in,FILE *out,HTNP ht,HFCODE hc[],char* SourceFilename,long bits|=chars[0]; for(i=1;i bits<<=1; //左移或实现 bits|=chars[i]; int i; BYTE bits=0; for(i=0;i free(code);//擦屁股 return OK; for(p=i,codelen=0;p!=2*n-2;p=htp[p].p,codelen++) {//循环到根节点为止 code[codelen]=(htp[htp[p].p].l==p?0:1);//第i位编码:p节点如果是其父亲的:左 BYTE *code=(BYTE*)malloc(n*BYTESIZE); //临时字符数组,为每一个字符的编码 申请一个字节的空间 int i,j,p,codelen; //codelen:临时字符数组长度变 孩子:0;右孩子:1 } if((hc[i].codestr=(BYTE *)malloc((codelen)*BYTESIZE))==NULL) { } hc[i].len=codelen; { } hc[i].codestr[j]=code[codelen-j-1]; //反转(因为原来是逆的) //该字符编码的码长 for(j=0;j //分配叶子节点huffman编码的空间,长 度为 . 精品文档 source_filesize) { //预处理 fseek(in,0L,SEEK_SET); fseek(out,0L,SEEK_SET); //定位读文件到文件开始处 //定位写文件到文件开始处 UINT i,Read_Counter,Write_Counter,zip_head=TAG_ZIGHEAD; //读缓存计数器,写缓存计数器,压缩文件头标 // //写字符计数器,huffman码字符计数器,复制字符计数器 BYTE write_char_counter,code_char_counter,copy_char_counter; BYTE read_buf[BUFFERSIZE],write_buf[BUFFERSIZE],write_chars[CHAR_BITS]; BYTE filename_size=strlen(SourceFilename); //文件名长度 //当前数据的huffman编码指针 HFCODE *Cur_HuffCode; 读缓存,写缓存,转换字符数组, fwrite(&zip_head,UINTSIZE,1,out); //写入文件头标示符 //写入源文件名长度 //写入源文件名 fwrite(&filename_size,BYTESIZE,1,out); fwrite(SourceFilename,sizeof(char),filename_size,out); fwrite(&source_filesize,sizeof(long),1,out); //写入源文件长度 for(i=HUFCODE_SIZE;i Write_Counter=write_char_counter=0; //写缓冲计数器以及写字符计数器清0 Read_Counter=BUFFERSIZE; //置读缓存字符数 //写huffman树的左右孩子(前HUFCODE_SIZE个节点无孩子,不写) fwrite(&(ht[i].l),sizeof(ht[i].l),1,out);//写入左孩子 fwrite(&(ht[i].r),sizeof(ht[i].r),1,out);//写入右孩子 //当读入的缓存字符数不等于缓存时,文件读完,退出循环 while(Read_Counter==BUFFERSIZE) { Read_Counter=fread(read_buf,1,BUFFERSIZE,in); //读入大小为BUFFSIZE的数据到读缓存 . 精品文档 者 //为每个缓存的数据找huffman编码 for(i=0;i //当前数据的huffman编码位置 //当前数据huffman编码字符的计数器清0 //当huffman编码字符的计数器等于码长时,转换完毕退出 while(code_char_counter!=Cur_HuffCode->len) { //获取本次复制字符的长度为 可用写字符长度与可用huffman编码长度中的较小 copy_char_counter= (CHAR_BITS-write_char_counter > Cur_HuffCode->len-code_char_counter : Cur_HuffCode->len-code_char_counter ? CHAR_BITS-write_char_counter); //复制一段字符 memcpy(write_chars+write_char_counter,Cur_HuffCode->codestr+code_char_counter,copy_ } fwrite(write_buf,1,Write_Counter,out);//写缓存中剩余数据输出到文件,擦屁股 //当写字符数组中还有字符未转换 if(write_char_counter!=0) { } } write_char_counter+=copy_char_counter; code_char_counter+=copy_char_counter; //当写字符计算器满=8时 if(write_char_counter==CHAR_BITS) { } write_char_counter=0;//写字符计数器清0 //当写缓存满时 write_buf[Write_Counter++]=Char2Bit(write_chars); if(Write_Counter==BUFFERSIZE) { } fwrite(write_buf,1,BUFFERSIZE,out);//输出到文件 Write_Counter=0;//写缓存清0 char_counter); //写字符计数器增加 //编码字符计数器增加 //转化写字符为二进制数并存入写缓存 . 精品文档 } } write_char_counter=Char2Bit(write_chars);//转化为二级制数据 fwrite(&write_char_counter,1,1,out);//输出到文件 return OK; //解压缩 int DeCompress(char *SourceFilename,char *DestinationFilename) { } int Initializing_Dezip(char *SourceFilename,FILE **inp,char *DestinationFilename,FILE **outp) { UINT zip_head; BYTE filename_size; char temp_filename[MAX_FILENAME]; //处理源文件 printf(\"Source Files:%s,\",SourceFilename); if ((*inp=fopen(SourceFilename,\"rb\"))==NULL) { Initializing_Dezip(SourceFilename,&in,DestinationFilename,&out); puts(\"File open Successfully...\"); fread(&Dst_Filesize,sizeof(long),1,in); // //初始 化文件处理环境 FILE *in,*out; // 定义输入输出流 short mini_ht[HUFCODE_SIZE*2-1][2]; long Dst_Filesize; 读取解压缩文件长度 printf(\"Expected Length: %ld\\n\",Dst_Filesize); puts(\"Establishing HuffmanTree...\"); ReadHuffmanTree(in,mini_ht); // 生成mini的huffmantree puts(\"Rebuild Successfully.Now Decompressing...\"); WriteDeZipFiles(in,out,mini_ht,ftell(in),Dst_Filesize); puts(\"Decompress Complete!\"); //擦屁股 fclose(in); fclose(out); return OK; // 解码压缩文件并生成解压缩文件 . 精品文档 } } return ERROR;//不能读文件 //读取源文件头,如果读入的文件头与常量不符,报错退出。 fread(&zip_head,UINTSIZE,1,*inp); if(zip_head!=TAG_ZIGHEAD) { } //处理解压缩文件名 if(DestinationFilename==NULL)//如果目标文件名未分配 { } else { } printf(\"Decompress Files:%s\\n\",DestinationFilename); if((*outp=fopen(DestinationFilename,\"wb\"))==NULL) { } return OK; return ERROR;//不能写文件 fread(&filename_size,BYTESIZE,1,*inp); fseek(*inp,filename_size,SEEK_CUR);//若分配了,直接跳过文件名信息 DestinationFilename = temp_filename; fread(&filename_size,BYTESIZE,1,*inp); //得到目标文件名 return ERROR;//非法的文件头 长度 fread(DestinationFilename,sizeof(char),filename_size,*inp); //得到目标文件名 DestinationFilename[filename_size]='\\0'; //添加结尾字符 void ReadHuffmanTree(FILE* in,short mini_ht[][2]) { } int WriteDeZipFiles(FILE *in,FILE* out,short mini_ht[][2],long bits_pos,long Dst_Filesize) { long cur_size; BYTE read_buf[BUFFERSIZE],write_buf[BUFFERSIZE],convert_bit; UINT Read_Counter,Write_Counter,cur_pos; int i; for(i=0;i mini_ht[i][0]=mini_ht[i][1]=UNUSE;//叶子节点无孩子 . 精品文档 } void ErrorReport(int error_code) { } printf(\"Error\"); fseek(in,bits_pos,SEEK_SET);//定位到写文件压缩码开始处 fseek(out,0L,SEEK_SET);//定位到写文件头部 //解码开始 Read_Counter=BUFFERSIZE-1;//置写缓存计数器 cur_size=Write_Counter=0;//当前写入文件长度与写缓存计数器为0 cur_pos=HUFCODE_SIZE*2-2;//当前huffman节点为根节点 //如果写入文件长度达到目标文件长度,文件写入完毕,退出 while(cur_size!=Dst_Filesize) { //若读缓存计数器满 cur_pos=((read_buf[Read_Counter]&convert_bit)==0?mini_ht[cur_pos][0]:mini_ht[cur_po } fwrite(write_buf,1,Write_Counter,out);//将写缓存中未输出的数据输出 return OK; } if(cur_pos cur_pos=HUFCODE_SIZE*2-2;//当前节点重置为最后根节点 if(++cur_size==Dst_Filesize) { } break;//如果当前写入文件长度到达预定值,跳出 fwrite(write_buf,1,BUFFERSIZE,out);//输出到文件 Write_Counter=0;//输入缓存计数器清0 if(++Read_Counter==BUFFERSIZE) { } //一次循环处理8位,即1字节 for(convert_bit=128;convert_bit!=0;convert_bit>>=1) { fread(read_buf,1,BUFFERSIZE,in);//读入到读缓存 Read_Counter=0;//读缓存计数器清0 s][1]);//按位查找huffmantree节点,0左,1右 . 精品文档 int main() { } return OK; case 'c': } {//压缩 printf(\"Input Source file: \"); gets(SRC); fflush(stdin); printf(\"\\nInput destination file:\"); gets(DST); fflush(stdin); Compress(SRC,DST); system(\"pause\"); char SRC[MAX_FILENAME],DST[MAX_FILENAME],choice='\\0'; printf(\"c for Compress,d for Decompress: \"); scanf(\"%c\",&choice); fflush(stdin); switch(choice) { };break; {//解压 printf(\"Input Source file: \"); gets(SRC); fflush(stdin); printf(\"Input destination file:\"); gets(DST); fflush(stdin); DeCompress(SRC,DST); system(\"pause\"); case 'd': }break; default:{ErrorReport(ERROR);}break; 四.界面设计 本实验提供基本的输入输出提示,包括压缩率的计算。 五.测试与分析 . 精品文档 压缩过程: 解压缩过程: 实际测试中压缩率达到了惊人的45%。对于某些文件。实际测试这是一个非常实用有效的工具。 六.实验收获及思考 收获: 确实是一个很有挑战的问题。关于文件写入部分有部分借鉴参考之处。 本代码的编写经历对知识的巩固有着巨大的作用。是一次很好的锻炼与实践。 . 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuowenda.com 版权所有 湘ICP备2023022495号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务