哈夫曼文件压缩
来源:互联网 发布:2016年詹姆斯生涯数据 编辑:程序博客网 时间:2024/05/22 07:00
用法:
压缩./ tar -y filename
解压缩 ./tar -j filename
==============编程思想==================
一、分析文件filename
按字符来分析
得出的结果,有哪些字符,每个字符出现的次数。
二、根据字符出现的次数,构建哈弗曼树。
得出字符的哈夫曼编码。
三、根据字符的哈夫曼编码,进行转换、压缩。创建压缩文件。(压缩文件里,要包含哈夫曼编码。)
四、需要的时候,读取压缩文件,从中读出哈夫曼编码和字符的对照表。然后解压缩。
数据结构:
保存字符次数和字符的数据结构
struct _symbol{
char character;
unsigned int number;
};
用一个数组保存所有字符的信息。
struct _symbol symbol_list[128];
哈夫曼树节点
struct node{
struct _symbol symbol;
struct node *left;
struct node *right;
};
哈夫曼编码
struct code{
char character;
char codebit[100];
#include <string.h>
//单个字符的结构体
struct _symbol{
char character;//字符
unsigned int number;//字符的次数
char huffcode[20]; //编码
};
//总结构体结构定义
struct _filestate{
char symbol_count;//多少种字符
struct _symbol symbol_array[128]; //结构体数组保存各种字符
};
//文件字符总结构体的变量定义
struct _filestate filestate;
//树节点
struct _node{
struct _node *parent;
struct _symbol symbol;
struct _node *left;
struct _node *right;
int idx; //在filestate中的索引
};
//创建一个数据类型
typedef struct _node node;
//创建节点
node *creatnode(struct _symbol symbol,int idx)
{
node *temp=malloc(sizeof(node));
temp->symbol=symbol;
temp->left=NULL;
temp->right=NULL;
temp->idx=idx;
temp->parent=NULL;
return temp;
}
//构建一棵哈弗曼树
node *createhaffman(struct _filestate *pfilestate)
{
//创建一维数组,里面保存指针
node **array=malloc(pfilestate->symbol_count*sizeof(node *));
node *temp=NULL;
int i;
for(i=0;i<pfilestate->symbol_count;i++)
{
array[i]=creatnode(pfilestate->symbol_array[i],i);
}
int j;
int minidx,secminidx;
for(i=0;i<pfilestate->symbol_count-1;i++)
{
j=0;
//找出第一个非空节点
while(array[j]==NULL)
{
j++;
}
minidx=j;
//拿第一个非空节点和后面的对比
for(j=0;j<pfilestate->symbol_count;j++)
{
if(array[j]&&array[j]->symbol.number<array[minidx]->symbol.number)
{
minidx=j;
}
}
j=0;
while(array[j]==NULL||j==minidx)
{
j++;
}
secminidx=j;
for(j=0;j<pfilestate->symbol_count;j++)
{
if(array[j]&&j!=minidx&&array[j]->symbol.number<array[secminidx]->symbol.number)
{
secminidx=j;
}
}
struct _symbol symbol={0,array[minidx]->symbol.number+array[secminidx]->symbol.number};
temp=creatnode(symbol,-1);
temp->left=array[minidx];
temp->right=array[secminidx];
temp->parent=NULL;
array[minidx]->parent=temp;
array[secminidx]->parent=temp;
array[minidx]=temp;
array[secminidx]=NULL;
}
free(array);
array=NULL;
return temp;
}
//判断是否叶子节点
int isleaf(node *cur)
{
if(cur->left==NULL&&cur->right==NULL)
return 1;
else
{
return 0;
}
}
//取得哈夫曼编码
void huffmancode(node *leaf,char code[20])
{
char a[20]={0};
node *temp=leaf;
int i=0;
while(temp->parent)
{
if(temp->parent->left==temp)
{
a[i]=2;
}
else
{
a[i]=1;
}
temp=temp->parent;
i++;
}
//以下将数组元素反转
int length=i;
char tmp=0;
for(i=0;i<length/2;i++)
{
tmp=a[i];
a[i]=a[length-i-1];
a[length-i-1]=tmp;
}
strcpy(code,a);
}
//取得叶子的哈夫曼码并填充到文件结构体里
void fillhuffmancode(node *root,struct _filestate *pfilestate)
{
int idx;
if(root)
{
if(isleaf(root))
{
idx=root->idx;
huffmancode(root,pfilestate->symbol_array[idx].huffcode);
}
else
{
fillhuffmancode(root->left,pfilestate);
fillhuffmancode(root->right,pfilestate);
}
}
}
//根据频率创建树且获取编码
void createandcoding(struct _filestate *pfilestate)
{
node *root=createhaffman(pfilestate);
fillhuffmancode(root,pfilestate);
int i,j;
for(i=0;i<pfilestate->symbol_count;i++)
{
printf("%c ",pfilestate->symbol_array[i].character);
for(j=0;j<20;j++)
{
printf("%d",pfilestate->symbol_array[i].huffcode[j]);
}
puts("\n");
}
}
//查字典,获得编码
void lookupdict(char character,struct _filestate *pfilestate,char bitcode[])
{
int i=0;
for(i=0;i<pfilestate->symbol_count;i++)
{
if(character==pfilestate->symbol_array[i].character)
{
strcpy(bitcode,pfilestate->symbol_array[i].huffcode);
break;
}
}
}
//写文件头
void writeheader(int fd,struct _filestate *pfilestate)
{
write(fd,pfilestate,sizeof(struct _filestate));
}
//读文件头
void readheader(int fd,struct _filestate *pfilestate)
{
read(fd,pfilestate,sizeof(struct _filestate));
}
//写编码
void writecode(int fd_r,int fd_w,struct _filestate *pfilestate)
{
int flgend=0;
//临时变量,用来保存取出来哈夫曼编码
char bitcode[20];
//临时变量
char temp;
char buffer;
//写字节的索引
int buf_idx=0;
//读到文件尾
int bitcode_idx=0;
//从源文件读一个字符
read(fd_r,&temp,1);
//从字典里查出字符的哈夫曼编码
lookupdict(temp,pfilestate,bitcode);
//一直读字符,直到文件的末尾
while(!flgend)
{
//用来写的字符,需要根据哈夫曼编码构建
buffer=0;
//现在指向buffer的第0位
buf_idx=0;
while(buf_idx<8)
{
if(bitcode[bitcode_idx]==2)
{
//把对应的位清零
buffer&=~(1<<buf_idx);
bitcode_idx++;
buf_idx++;
}
else if(bitcode[bitcode_idx]==1)
{
//给对应的位置1
buffer|=(1<<buf_idx);
bitcode_idx++;
buf_idx++;
}
else//如果编码数组里的编码读完了
{
//再取一个字符
if((read(fd_r,&temp,1))==1)
{
//根据字符取得哈夫曼码
lookupdict(temp,pfilestate,bitcode);
bitcode_idx=0;
}
else
{
flgend=1;
break;
}
}
}
write(fd_w,&buffer,1);
}
}
//写到文件中
void writetofile(char *filename,struct _filestate *pfilestate)
{
int fd1=open(filename,O_RDONLY);
if(fd1<0)
{
perror("open");
exit(EXIT_FAILURE);
}
char *newfilename=strcat(filename,".aar");
int fd2=open(newfilename,O_CREAT|O_TRUNC|O_WRONLY,0666);
if(fd2<0)
{
perror("open");
exit(EXIT_FAILURE);
}
//将字典结构体先写到文件里面,方便解压时读取
writeheader(fd2,pfilestate);
//写编码
writecode(fd1,fd2,pfilestate);
close(fd1);
close(fd2);
}
//计算字符的个数
void coutsymbol(struct _filestate *pfilestate,char ch)
{
int i;
int flagexist=0;
for(i=0;i<pfilestate->symbol_count;i++)
{
//如果字符已被保存过
if(ch==pfilestate->symbol_array[i].character)
{
//则让该字符的数量+1
pfilestate->symbol_array[i].number++;
flagexist=1;
break;
}
}
//如果字符没有被保存过
if(!flagexist)
{
pfilestate->symbol_array[pfilestate->symbol_count].character=ch;
pfilestate->symbol_array[pfilestate->symbol_count].number=1;
pfilestate->symbol_count++;
}
}
//分析文件,得到各字符的次数
void analyse(char *filename)
{
//打开文件
int fd=open(filename,O_RDONLY);
if(fd<0)
{
return;
}
char temp;
//读取一个字符,直到文件结束
while((read(fd,&temp,1))==1)
{
//把单个字符交给更细的分析函数,
//该函数将字符和现有的数据进行对比,然后计算数量
coutsymbol(&filestate,temp);
}
close(fd);
}
//压缩
void compress(char *filename)
{
//分析文件
analyse(filename);
//创建哈夫曼树且取得每个字母的编码
createandcoding(&filestate);
//将文件的每个字母翻译成编码,写入压缩文件
writetofile(filename,&filestate);
}
//---------------------------------------------------
//写解压缩文件
void writedecompressfile(int fd, char *filename,node *root)
{
char *newfilename=strcat(filename,".un");
node *tmpnode=root;
int fd_w=open(newfilename,O_CREAT|O_WRONLY|O_TRUNC,0600);
if(fd_w<0)
{
perror("open");
return;
}
char temp;
int bit_idx;
while(read(fd,&temp,1)==1)
{
bit_idx=0;
while(bit_idx<8)
{
if(temp&(1<<bit_idx))
{
tmpnode=tmpnode->right;
}
else
{
tmpnode=tmpnode->left;
}
if(isleaf(tmpnode))
{
write(fd_w,&(tmpnode->symbol.character),1);
tmpnode=root;
}
bit_idx++;
}
}
close(fd_w);
}
//解压
void decompress(char *filename)
{
int fd=open(filename,O_RDWR);
if(fd<0)
{
perror("open");
return;
}
memset(&filestate,0,sizeof(filestate));
//读文件头
readheader(fd,&filestate);
//创建哈弗曼树
node *root=NULL;
root=createhaffman(&filestate);
writedecompressfile(fd,filename,root);
close(fd);
}
//------------------------------------------------
//主函数
int main(int argc,char const *argv[])
{
char *filename=(char *)argv[2];
if(argc<3)
{
puts("用法:./tar -y[-j] filename");
exit(EXIT_FAILURE);
}
//选项为-y,压缩
if(strcmp(argv[1],"-y")==0)
{
compress(filename);
}
//选项为-j,解压
else if(strcmp(argv[1],"-j")==0)
{
decompress(filename);
}
else
{
puts("参数错误!");
exit(EXIT_FAILURE);
}
}
压缩./ tar -y filename
解压缩 ./tar -j filename
==============编程思想==================
一、分析文件filename
按字符来分析
得出的结果,有哪些字符,每个字符出现的次数。
二、根据字符出现的次数,构建哈弗曼树。
得出字符的哈夫曼编码。
三、根据字符的哈夫曼编码,进行转换、压缩。创建压缩文件。(压缩文件里,要包含哈夫曼编码。)
四、需要的时候,读取压缩文件,从中读出哈夫曼编码和字符的对照表。然后解压缩。
数据结构:
保存字符次数和字符的数据结构
struct _symbol{
char character;
unsigned int number;
};
用一个数组保存所有字符的信息。
struct _symbol symbol_list[128];
哈夫曼树节点
struct node{
struct _symbol symbol;
struct node *left;
struct node *right;
};
哈夫曼编码
struct code{
char character;
char codebit[100];
};
==================================================源代码======================================================
#include "header.h"#include <string.h>
//单个字符的结构体
struct _symbol{
char character;//字符
unsigned int number;//字符的次数
char huffcode[20]; //编码
};
//总结构体结构定义
struct _filestate{
char symbol_count;//多少种字符
struct _symbol symbol_array[128]; //结构体数组保存各种字符
};
//文件字符总结构体的变量定义
struct _filestate filestate;
//树节点
struct _node{
struct _node *parent;
struct _symbol symbol;
struct _node *left;
struct _node *right;
int idx; //在filestate中的索引
};
//创建一个数据类型
typedef struct _node node;
//创建节点
node *creatnode(struct _symbol symbol,int idx)
{
node *temp=malloc(sizeof(node));
temp->symbol=symbol;
temp->left=NULL;
temp->right=NULL;
temp->idx=idx;
temp->parent=NULL;
return temp;
}
//构建一棵哈弗曼树
node *createhaffman(struct _filestate *pfilestate)
{
//创建一维数组,里面保存指针
node **array=malloc(pfilestate->symbol_count*sizeof(node *));
node *temp=NULL;
int i;
for(i=0;i<pfilestate->symbol_count;i++)
{
array[i]=creatnode(pfilestate->symbol_array[i],i);
}
int j;
int minidx,secminidx;
for(i=0;i<pfilestate->symbol_count-1;i++)
{
j=0;
//找出第一个非空节点
while(array[j]==NULL)
{
j++;
}
minidx=j;
//拿第一个非空节点和后面的对比
for(j=0;j<pfilestate->symbol_count;j++)
{
if(array[j]&&array[j]->symbol.number<array[minidx]->symbol.number)
{
minidx=j;
}
}
j=0;
while(array[j]==NULL||j==minidx)
{
j++;
}
secminidx=j;
for(j=0;j<pfilestate->symbol_count;j++)
{
if(array[j]&&j!=minidx&&array[j]->symbol.number<array[secminidx]->symbol.number)
{
secminidx=j;
}
}
struct _symbol symbol={0,array[minidx]->symbol.number+array[secminidx]->symbol.number};
temp=creatnode(symbol,-1);
temp->left=array[minidx];
temp->right=array[secminidx];
temp->parent=NULL;
array[minidx]->parent=temp;
array[secminidx]->parent=temp;
array[minidx]=temp;
array[secminidx]=NULL;
}
free(array);
array=NULL;
return temp;
}
//判断是否叶子节点
int isleaf(node *cur)
{
if(cur->left==NULL&&cur->right==NULL)
return 1;
else
{
return 0;
}
}
//取得哈夫曼编码
void huffmancode(node *leaf,char code[20])
{
char a[20]={0};
node *temp=leaf;
int i=0;
while(temp->parent)
{
if(temp->parent->left==temp)
{
a[i]=2;
}
else
{
a[i]=1;
}
temp=temp->parent;
i++;
}
//以下将数组元素反转
int length=i;
char tmp=0;
for(i=0;i<length/2;i++)
{
tmp=a[i];
a[i]=a[length-i-1];
a[length-i-1]=tmp;
}
strcpy(code,a);
}
//取得叶子的哈夫曼码并填充到文件结构体里
void fillhuffmancode(node *root,struct _filestate *pfilestate)
{
int idx;
if(root)
{
if(isleaf(root))
{
idx=root->idx;
huffmancode(root,pfilestate->symbol_array[idx].huffcode);
}
else
{
fillhuffmancode(root->left,pfilestate);
fillhuffmancode(root->right,pfilestate);
}
}
}
//根据频率创建树且获取编码
void createandcoding(struct _filestate *pfilestate)
{
node *root=createhaffman(pfilestate);
fillhuffmancode(root,pfilestate);
int i,j;
for(i=0;i<pfilestate->symbol_count;i++)
{
printf("%c ",pfilestate->symbol_array[i].character);
for(j=0;j<20;j++)
{
printf("%d",pfilestate->symbol_array[i].huffcode[j]);
}
puts("\n");
}
}
//查字典,获得编码
void lookupdict(char character,struct _filestate *pfilestate,char bitcode[])
{
int i=0;
for(i=0;i<pfilestate->symbol_count;i++)
{
if(character==pfilestate->symbol_array[i].character)
{
strcpy(bitcode,pfilestate->symbol_array[i].huffcode);
break;
}
}
}
//写文件头
void writeheader(int fd,struct _filestate *pfilestate)
{
write(fd,pfilestate,sizeof(struct _filestate));
}
//读文件头
void readheader(int fd,struct _filestate *pfilestate)
{
read(fd,pfilestate,sizeof(struct _filestate));
}
//写编码
void writecode(int fd_r,int fd_w,struct _filestate *pfilestate)
{
int flgend=0;
//临时变量,用来保存取出来哈夫曼编码
char bitcode[20];
//临时变量
char temp;
char buffer;
//写字节的索引
int buf_idx=0;
//读到文件尾
int bitcode_idx=0;
//从源文件读一个字符
read(fd_r,&temp,1);
//从字典里查出字符的哈夫曼编码
lookupdict(temp,pfilestate,bitcode);
//一直读字符,直到文件的末尾
while(!flgend)
{
//用来写的字符,需要根据哈夫曼编码构建
buffer=0;
//现在指向buffer的第0位
buf_idx=0;
while(buf_idx<8)
{
if(bitcode[bitcode_idx]==2)
{
//把对应的位清零
buffer&=~(1<<buf_idx);
bitcode_idx++;
buf_idx++;
}
else if(bitcode[bitcode_idx]==1)
{
//给对应的位置1
buffer|=(1<<buf_idx);
bitcode_idx++;
buf_idx++;
}
else//如果编码数组里的编码读完了
{
//再取一个字符
if((read(fd_r,&temp,1))==1)
{
//根据字符取得哈夫曼码
lookupdict(temp,pfilestate,bitcode);
bitcode_idx=0;
}
else
{
flgend=1;
break;
}
}
}
write(fd_w,&buffer,1);
}
}
//写到文件中
void writetofile(char *filename,struct _filestate *pfilestate)
{
int fd1=open(filename,O_RDONLY);
if(fd1<0)
{
perror("open");
exit(EXIT_FAILURE);
}
char *newfilename=strcat(filename,".aar");
int fd2=open(newfilename,O_CREAT|O_TRUNC|O_WRONLY,0666);
if(fd2<0)
{
perror("open");
exit(EXIT_FAILURE);
}
//将字典结构体先写到文件里面,方便解压时读取
writeheader(fd2,pfilestate);
//写编码
writecode(fd1,fd2,pfilestate);
close(fd1);
close(fd2);
}
//计算字符的个数
void coutsymbol(struct _filestate *pfilestate,char ch)
{
int i;
int flagexist=0;
for(i=0;i<pfilestate->symbol_count;i++)
{
//如果字符已被保存过
if(ch==pfilestate->symbol_array[i].character)
{
//则让该字符的数量+1
pfilestate->symbol_array[i].number++;
flagexist=1;
break;
}
}
//如果字符没有被保存过
if(!flagexist)
{
pfilestate->symbol_array[pfilestate->symbol_count].character=ch;
pfilestate->symbol_array[pfilestate->symbol_count].number=1;
pfilestate->symbol_count++;
}
}
//分析文件,得到各字符的次数
void analyse(char *filename)
{
//打开文件
int fd=open(filename,O_RDONLY);
if(fd<0)
{
return;
}
char temp;
//读取一个字符,直到文件结束
while((read(fd,&temp,1))==1)
{
//把单个字符交给更细的分析函数,
//该函数将字符和现有的数据进行对比,然后计算数量
coutsymbol(&filestate,temp);
}
close(fd);
}
//压缩
void compress(char *filename)
{
//分析文件
analyse(filename);
//创建哈夫曼树且取得每个字母的编码
createandcoding(&filestate);
//将文件的每个字母翻译成编码,写入压缩文件
writetofile(filename,&filestate);
}
//---------------------------------------------------
//写解压缩文件
void writedecompressfile(int fd, char *filename,node *root)
{
char *newfilename=strcat(filename,".un");
node *tmpnode=root;
int fd_w=open(newfilename,O_CREAT|O_WRONLY|O_TRUNC,0600);
if(fd_w<0)
{
perror("open");
return;
}
char temp;
int bit_idx;
while(read(fd,&temp,1)==1)
{
bit_idx=0;
while(bit_idx<8)
{
if(temp&(1<<bit_idx))
{
tmpnode=tmpnode->right;
}
else
{
tmpnode=tmpnode->left;
}
if(isleaf(tmpnode))
{
write(fd_w,&(tmpnode->symbol.character),1);
tmpnode=root;
}
bit_idx++;
}
}
close(fd_w);
}
//解压
void decompress(char *filename)
{
int fd=open(filename,O_RDWR);
if(fd<0)
{
perror("open");
return;
}
memset(&filestate,0,sizeof(filestate));
//读文件头
readheader(fd,&filestate);
//创建哈弗曼树
node *root=NULL;
root=createhaffman(&filestate);
writedecompressfile(fd,filename,root);
close(fd);
}
//------------------------------------------------
//主函数
int main(int argc,char const *argv[])
{
char *filename=(char *)argv[2];
if(argc<3)
{
puts("用法:./tar -y[-j] filename");
exit(EXIT_FAILURE);
}
//选项为-y,压缩
if(strcmp(argv[1],"-y")==0)
{
compress(filename);
}
//选项为-j,解压
else if(strcmp(argv[1],"-j")==0)
{
decompress(filename);
}
else
{
puts("参数错误!");
exit(EXIT_FAILURE);
}
}
0 0
- 哈夫曼文件压缩
- 哈夫曼压缩解压缩任意文件
- 文件压缩和解压缩
- 哈夫曼文件压缩源文件+头文件
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- localstorage、sessionstorage和cookie的区别(面试常问)
- shell脚本直接执行没有问题,crontab定时执行失败的解决方法
- 怎么关闭谷歌浏览器自动断点调试?打开网页按F12之后,打开任何网页都会自动断点调试js
- [编程题] 优雅的点
- 自题小像-鲁迅
- 哈夫曼文件压缩
- [南阳OJ-No.57]6174问题|数学黑洞
- 精进-向未知的无限逼近
- Nginx实现虚拟主机配置
- git设置代理和取消代理
- Codeforces 450B f【n】=f【n-1】-f【n-2】(矩阵快速幂,裸题)
- Cesium原理篇:3D Tiles(1)渲染调度
- C++效率编程心得
- 计算机视觉——欧氏距离变换EDT