数据结构文件压缩.docx
《数据结构文件压缩.docx》由会员分享,可在线阅读,更多相关《数据结构文件压缩.docx(22页珍藏版)》请在冰豆网上搜索。
数据结构文件压缩
数据结构实验报告
实验名称:
文件压缩
实验类型:
综合性试验
班级:
20112111
学号:
2011211107
姓名:
冯若航
实验日期:
2003.6.19下午4:
00
1.问题描述
文件压缩
基本要求
哈夫曼编码是一种常用的数据压缩技术,对数据文件进行哈夫曼编码可大大缩短文件的传输长度,提高信道利用率及传输效率。
要求采用哈夫曼编码原理,统计文本文件中字符出现的词频,以词频作为权值,对文件进行哈夫曼编码以达到压缩文件的目的,再用哈夫曼编码进行译码解压缩。
●统计待压缩的文本文件中各字符的词频,以词频为权值建立哈夫曼树,并将该哈夫曼树保存到文件HufTree.dat中。
●根据哈夫曼树(保存在HufTree.dat中)对每个字符进行哈夫曼编码,并将字符编码保存到HufCode.txt文件中。
●压缩:
根据哈夫曼编码,将源文件进行编码得到压缩文件CodeFile.dat。
●解压:
将CodeFile.dat文件利用哈夫曼树译码解压,恢复为源文件。
2.数据结构设计
此类问题,应设计文件的数据结构。
*4压缩头标记
*1文件名长度
*ns文件名
*4源文件长度
*1020huffman树
*1021~EOF文件内容
赫夫曼树节点的数据结构
typedefstructnode
{
longw;//权
shortp,l,r;//父亲,左孩子,右孩子
}HTNODE,*HTNP;//霍夫曼树的结点
赫夫曼编码数组元素的数据结构设计
typedefstructhuffman_code
{
BYTElen;//长度
BYTE*codestr;//字符串
}HFCODE;//霍夫曼编码数组元素
3.算法设计
源代码
#define_CRT_SECURE_NO_DEPRECATE
#include
#include
#include
typedefunsignedintUINT;
typedefunsignedcharBYTE;
typedefstructnode
{
longw;//权
shortp,l,r;//父亲,左孩子,右孩子
}HTNODE,*HTNP;//霍夫曼树的结点
typedefstructhuffman_code
{
BYTElen;//长度
BYTE*codestr;//字符串
}HFCODE;//霍夫曼编码数组元素
#defineOK1
#defineERROR-1
#defineUNUSE-1//未链接节点标志
#defineCHAR_BITS8//一个字符中的位数
#defineINT_BITS32//一个整型中的位数
#defineHUFCODE_SIZE256//霍夫曼编码个数
#defineBUFFERSIZE256//缓冲区大小大小
#defineUINTSIZEsizeof(UINT)
#defineBYTESIZEsizeof(BYTE)
#defineTAG_ZIGHEAD0xFFFFFFFF//压缩文件头标
#defineMAX_FILENAME512
//函数声明
//压缩模块
intCompress(char*SourceFilename,char*DestinationFilename);//压缩调用
intInitializing(char*SourceFilename,FILE**inp,char*DestinationFilename,FILE**outp);//初始化文件工作环境
longAnalysisFiles(FILE*in,longfrequency[]);//计算每个不同字节的频率以及所有的字节数
intCreateHuffmanTree(longw[],intn,HTNODEht[]);//生成霍夫曼树
intHuffmanTreeCoding(HTNPhtp,intn,HFCODEhc[]);//霍夫曼编码
intSearch(HTNPht,intn);//查找当前最小权值的霍夫曼树节点并置为占用
BYTEChar2Bit(constBYTEchars[CHAR_BITS]);//将一个字符数组转换成二进制数字
intSearch(HTNPht,intn);//查找当前最小权值的霍夫曼树节点并置为占用
intWriteZipFiles(FILE*in,FILE*out,HTNPht,HFCODEhc[],char*SourceFilename,longsource_filesize);//写压缩文件
//解压缩模块
intDeCompress(char*SourceFilename,char*DestinationFilename);//解压缩调用
intInitializing_Dezip(char*SourceFilename,FILE**inp,char*DestinationFilename,FILE**outp);//为处理解压缩流程初始化文件
voidReadHuffmanTree(FILE*in,shortmini_ht[][2]);//从待解压缩文件中读取huffman树
intWriteDeZipFiles(FILE*in,FILE*out,shortmini_ht[][2],longbits_pos,longDst_Filesize);//写解压缩文件
voidErrorReport(interror_code);//报错
//函数定义
//函数实现
//压缩
intCompress(char*SourceFilename,char*DestinationFilename)
{
FILE*in,*out;//输入输出流
inti;//计数变量
floatCompress_rate;//存放压缩率
HFCODEhc[HUFCODE_SIZE];//存放256个字符的huffman编码
HTNODEht[HUFCODE_SIZE*2-1];//256个字符的huffman树需要2*256-1=511个节点。
longfrequency[HUFCODE_SIZE],source_filesize,Dst_Filesize=0;//字符频率数组,源文件,目标文件。
//处理待压缩与压缩文件文件
Initializing(SourceFilename,&in,DestinationFilename,&out);
puts("LoadingFiles...");
//处理各个字符的频率,并输出原始文件长度
source_filesize=AnalysisFiles(in,frequency);
puts("LoadingComplete!
NowAnalysising...");
printf("Originalsize:
%ldByte\n",source_filesize);
//创建Huffman树
CreateHuffmanTree(frequency,HUFCODE_SIZE,ht);
//Huffman编码
puts("HuffmanTreeInitializedSuccessfully,HuffmanCodingProcessing...");
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{//huffmantree长度
Dst_Filesize+=2*sizeof(short);
}
Dst_Filesize+=strlen(SourceFilename)+1;//源文件名占用空间
Dst_Filesize+=sizeof(long);//源文件名长度信息占用空间
Dst_Filesize+=UINTSIZE;//文件头长度
Compress_rate=(float)Dst_Filesize/source_filesize;//压缩率
puts("CodingSuccessfully.Nowproducingzipfiles...");
printf("CompressedFileSize:
%ldByte,radiation:
%%%.2lf\n",Dst_Filesize,Compress_rate*100);
//生成压缩文件
WriteZipFiles(in,out,ht,hc,SourceFilename,source_filesize);
puts("CompressComplete!
");
//擦屁股
fclose(in);
fclose(out);//关闭文件
for(i=0;i{
free(hc[i].codestr);
}
returnOK;
}
intInitializing(char*SourceFilename,FILE**inp,char*DestinationFilename,FILE**outp)
{
if(strcmp(SourceFilename,DestinationFilename)==0)
{//重名判断
returnERROR;
}
printf("SourceFile:
%s,DestinationFile:
%s\n",SourceFilename,DestinationFilename);
if((*outp=fopen(DestinationFilename,"wb"))==NULL)
{//文件打开错误
returnERROR;
}
if((*inp=fopen(SourceFilename,"rb"))==NULL)
{//文件打开错误
returnERROR;
}
returnOK;
}
longAnalysisFiles(FILE*in,longfrequency[])
{
inti,read_len;
BYTEbuf[BUFFERSIZE];//缓冲区
longfilesize;//文件总长
for(i=0;i{
frequency[i]=0;//初始化所有字符频率为0
}
fseek(in,0L,SEEK_SET);//将文件定位符指向文件开头
read_len=BUFFERSIZE;//设置读入长度=缓冲区长度
while(read_len==BUFFERSIZE)//每当读取字符长度达到缓冲区长度时
{
read_len=fread(buf,1,BUFFERSIZE,in);
for(i=0;i{
frequency[*(buf+i)]++;//统计字频
}
}
for(i=0,filesize=0;i{
filesize+=frequency[i];//计算文件长度,计算方法是把所有字符的频数相加
}
returnfilesize;
}
intSearch(HTNPht,intn)
{
inti,x;
for(x=0;x{
if(ht[x].p==UNUSE)break;//找到第一个没有使用的叶子节点,跳出
}
for(i=x;i{
if(ht[i].p==UNUSE&&ht[i].w{
x=i;//找权值最小的叶子节点
}
}
ht[x].p=-2;//将叶子节点占用
returnx;
}
intCreateHuffmanTree(longw[],intn,HTNODEht[])
{
intm,i,s1,s2;
if(n<1)returnERROR;
m=2*n-1;//霍夫曼树节点总数=叶子数*2-1
if(ht==NULL)returnERROR;
for(i=0;i{
ht[i].w=w[i],ht[i].p=ht[i].l=ht[i].r=UNUSE;//初始化叶子节点
}
for(;i{
ht[i].w=ht[i].p=ht[i].l=ht[i].r=UNUSE;//初始化分支节点
}
for(i=n;i{//循环至m-n个分支节点全部被使用完为止
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;//设置权为两个孩子权之和
}
returnOK;
}
intHuffmanTreeCoding(HTNPhtp,intn,HFCODEhc[])
{
inti,j,p,codelen;//codelen:
临时字符数组长度变量
BYTE*code=(BYTE*)malloc(n*BYTESIZE);//临时字符数组,为每一个字符的编码申请一个字节的空间
for(i=0;i{//从当前节点到根节点逆向求huffman编码,遍历所有叶子节点。
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节点如果是其父亲的:
左孩子:
0;右孩子:
1
}
if((hc[i].codestr=(BYTE*)malloc((codelen)*BYTESIZE))==NULL)
{
returnERROR;//分配叶子节点huffman编码的空间,长度为
}
hc[i].len=codelen;//该字符编码的码长
for(j=0;j{
hc[i].codestr[j]=code[codelen-j-1];//反转(因为原来是逆的)
}
}
free(code);//擦屁股
returnOK;
}
BYTEChar2Bit(constBYTEchars[CHAR_BITS])
{
inti;
BYTEbits=0;
bits|=chars[0];
for(i=1;i{
bits<<=1;//左移或实现
bits|=chars[i];
}
returnbits;
}
intWriteZipFiles(FILE*in,FILE*out,HTNPht,HFCODEhc[],char*SourceFilename,longsource_filesize)
{
UINTi,Read_Counter,Write_Counter,zip_head=TAG_ZIGHEAD;//读缓存计数器,写缓存计数器,压缩文件头标
BYTEwrite_char_counter,code_char_counter,copy_char_counter;//写字符计数器,huffman码字符计数器,复制字符计数器
BYTEread_buf[BUFFERSIZE],write_buf[BUFFERSIZE],write_chars[CHAR_BITS];//读缓存,写缓存,转换字符数组,
BYTEfilename_size=strlen(SourceFilename);//文件名长度
HFCODE*Cur_HuffCode;//当前数据的huffman编码指针
//预处理
fseek(in,0L,SEEK_SET);//定位读文件到文件开始处
fseek(out,0L,SEEK_SET);//定位写文件到文件开始处
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{//写huffman树的左右孩子(前HUFCODE_SIZE个节点无孩子,不写)
fwrite(&(ht[i].l),sizeof(ht[i].l),1,out);//写入左孩子
fwrite(&(ht[i].r),sizeof(ht[i].r),1,out);//写入右孩子
}
//写正文
Write_Counter=write_char_counter=0;//写缓冲计数器以及写字符计数器清0
Read_Counter=BUFFERSIZE;//置读缓存字符数
//当读入的缓存字符数不等于缓存时,文件读完,退出循环
while(Read_Counter==BUFFERSIZE)
{
Read_Counter=fread(read_buf,1,BUFFERSIZE,in);//读入大小为BUFFSIZE的数据到读缓存
//为每个缓存的数据找huffman编码
for(i=0;i{
Cur_HuffCode=&hc[read_buf[i]];//当前数据的huffman编码位置
code_char_counter=0;//当前数据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_char_counter);
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
}
}
}
}
}
fwrite(write_buf,1,Write_Counter,out);//写缓存中剩余数据输出到文件,擦屁股
//当写字符数组中还有字符未转换
if(write_char_counter!
=0)
{
write_char_counter=Char2Bit(write_chars);//转化为二级制数据
fwrite(&write_char_counter,1,1,out);//输出到文件
}
returnOK;
}
//解压缩
intDeCompr