用哈弗曼编码实现文件压缩和解压(改进集成版1.0)

Posted tookkke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用哈弗曼编码实现文件压缩和解压(改进集成版1.0)相关的知识,希望对你有一定的参考价值。

    将压缩和解压放在了一个程序里面,并加入了进度显示功能。

    并不完善,因为使用'\\b'回退符来达到进度的数字能够变化,可是在需要操作的文件较小时会闪动比较严重,还会消耗多余的资源在显示上面,所以执行效率并不算高,可能在学会其他知识,比如图形化界面时我会再来改进它。

    其他内容在旧版中有写,说多了都是泪 :http://blog.csdn.net/tookkke/article/details/50529838

#include <cstdio>                                     /************/
#include <cstring>                                    /*    by    */
#include <iostream>                                   /*   kkke   */
#include <algorithm>                                  /************/
#include <queue>                                                       //2016.1.24
#include <conio.h>

#define MAX_WORD (65536)
#define MAX_BYTE (256)

using namespace std;

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef unsigned long long ULL;

char in_file_name[80];
char out_file_name[80];
int cnt[MAX_BYTE*2];//number of each BYTE
int filesize;
ULL bit_cnt;
int kkke[MAX_BYTE][MAX_BYTE];
int kkke1[MAX_BYTE];
int hehe[MAX_BYTE];

const int tree_size=MAX_BYTE*2;
struct the_tree
	int son[2];
tree[MAX_BYTE*2];//root is 1

struct cmp
	bool operator()(int a,int b)
	
		return cnt[a]>cnt[b];
	
;

void read_data();
void build_tree();
void dfs(int k,int b);
void output();

void kdecompress()

	system("cls");
	printf("解压\\n");
	printf("请输入待解压文件名(不加.kcps):");
	scanf("%s",in_file_name);
	printf("请选择 输出文件名(会覆盖重名文件):\\n");
	printf("    1. %s\\n",in_file_name);
	printf("    2. 手动输入\\n");
	char c;
	while((c=getch())!='1'&&c!='2');
	if(c=='1')strcpy(out_file_name,in_file_name);
	else
	
		printf("请输入输出文件名:");
		scanf("%s",out_file_name);
	
	strcat(in_file_name,".kcps");
	printf("将会生成 %s\\n",out_file_name);
	printf("    取消: ESC\\n");
	printf("    确认: 回车\\n");
	while((c=getch())!=13&&c!=27);
	if(c==27)return;
	
	FILE *infp=fopen(in_file_name,"rb");
	if(infp==NULL)
	
		printf("待解压文件 %s 打开失败\\n",in_file_name);
		exit(1);
	
	FILE *outfp=fopen(out_file_name,"wb");
	if(outfp==NULL)
	
		printf("解压文件 %s 创建失败\\n",out_file_name);
		exit(1);
	
	
	for(int i=1;i<tree_size;i++)fread(&tree[i],sizeof(tree[i]),1,infp);
	fread(&bit_cnt,sizeof(bit_cnt),1,infp);
	
	BYTE a1;
	int a2=8;
	int nown=1;
	int b=0;
	system("cls");
	printf("正在解压文件... %3d.%02d%%",b/100,b%100);
	for(long long i=1;i<=bit_cnt;i++)
	
		if(a2==8)
		
			a2=0;
		    fread(&a1,sizeof(a1),1,infp);
		
		if(a1&((BYTE)1<<a2))nown=tree[nown].son[1];
		else nown=tree[nown].son[0];
		if(nown>=MAX_BYTE)
		
			BYTE aa=nown-MAX_BYTE;
			fwrite(&aa,sizeof(aa),1,outfp);
			nown=1;
		
		a2++;
		if(b==i*10000/bit_cnt)continue;
	    b=i*10000/bit_cnt;
	    for(int k=0;k<7;k++)putchar('\\b');
	    printf("%3d.%02d%%",b/100,b%100);
	
	putchar('\\n');
	if(fclose(infp))
	
		printf("关闭输入文件 %s 失败\\n",in_file_name);
		exit(1);
	
	if(fclose(outfp))
	
		printf("关闭输出文件 %s 失败\\n",out_file_name);
		exit(1);
	
	system("pause");


void kcompress()

	system("cls");
	printf("压缩\\n");
	printf("请输入待压缩文件名:");
	scanf("%s",in_file_name);
	printf("请选择 输出文件名(会覆盖重名文件):\\n");
	printf("    1. %s.kcps\\n",in_file_name);
	printf("    2. 手动输入(.kcps)\\n");
	char c;
	while((c=getch())!='1'&&c!='2');
	if(c=='1')strcpy(out_file_name,in_file_name);
	else
	
		printf("请输入输出文件名:");
		scanf("%s",out_file_name);
	
	strcat(out_file_name,".kcps");
	printf("将会生成 %s\\n",out_file_name);
	printf("    取消: ESC\\n");
	printf("    确认: 回车\\n");
	while((c=getch())!=13&&c!=27);
	if(c==27)return;
	
	read_data();
	build_tree();
	dfs(1,0);
	output();
	system("pause");


int main()

	while(true)
	
		system("cls");
		printf("@kkke随便写写的压缩程序:\\n");
		printf("请置于待操作文件同一文件夹\\n");
		printf("选择    1. 压缩\\n");
		printf("        2. 解压\\n");
		char c;
		while((c=getch())!='1'&&c!='2');
		if(c=='1')
		
			printf("已选择 压缩\\n");
			printf("    取消: ESC\\n");
			printf("    确认: 回车\\n");
			while((c=getch())!=13&&c!=27);
			if(c==27)continue;
		    kcompress();
		
		else
		
			printf("已选择 解压\\n");
			printf("    取消: ESC\\n");
			printf("    确认: 回车\\n");
			while((c=getch())!=13&&c!=27);
			if(c==27)continue;
		    kdecompress();
		
	
	return 0;


void output()

	FILE *infp=fopen(in_file_name,"rb");
	if(infp==NULL)
	
		printf("待压缩文件 %s 打开失败\\n",in_file_name);
		exit(1);
	
	FILE *outfp=fopen(out_file_name,"wb");
	if(outfp==NULL)
	
		printf("压缩文件 %s 创建失败\\n",out_file_name);
		exit(1);
	
	
	for(int i=1;i<tree_size;i++)fwrite(&tree[i],sizeof(tree[i]),1,outfp);
	bit_cnt=0ULL;
	for(int i=0;i<MAX_BYTE;i++)bit_cnt+=(ULL)hehe[i]*(ULL)cnt[i+MAX_BYTE];
	fwrite(&bit_cnt,sizeof(bit_cnt),1,outfp);
	
	int b=0;
	system("cls");
	printf("生成文件总大小: %.3f KB\\n",((double)bit_cnt+8.0*(double)sizeof(tree))/8192.0);
	printf("压缩率: %.3f%%\\n",((double)bit_cnt+8.0*(double)sizeof(tree))/0.08/(double)filesize);
	printf("正在生成压缩文件... %3d.%02d%%",b/100,b%100);
	
	BYTE a;
	BYTE a1=0;
	int a2=0;
	for(int i=1;i<=filesize;i++)
	
		fread(&a,sizeof(a),1,infp);
		for(int j=0;j<hehe[a];j++)
		
			if(kkke[a][j])a1|=((BYTE)1<<a2);
			a2++;
			if(a2==8)
			
				a2=0;
				fwrite(&a1,sizeof(a1),1,outfp);
				a1=0;
			
		
		if(b==(ULL)i*10000/filesize)continue;
	    b=(ULL)i*10000/filesize;
	    for(int k=0;k<7;k++)putchar('\\b');
	    printf("%3d.%02d%%",b/100,b%100);
	
	putchar('\\n');
	if(a2)fwrite(&a1,sizeof(a1),1,outfp);
	if(fclose(infp))
	
		printf("关闭输入文件 %s 失败\\n",in_file_name);
		exit(1);
	
	if(fclose(outfp))
	
		printf("关闭输出文件 %s 失败\\n",out_file_name);
		exit(1);
	


void dfs(int k,int b)

	if(k>=MAX_BYTE)
	
		k-=MAX_BYTE;
		for(int i=0;i<b;i++)kkke[k][i]=kkke1[i];
		hehe[k]=b;
	
	else
	
		kkke1[b]=0;
		dfs(tree[k].son[0],b+1);
		kkke1[b]=1;
		dfs(tree[k].son[1],b+1);
	


void build_tree()

    system("cls");
	printf("正在分析文件...\\n");
	priority_queue<int,vector<int>,cmp>q;
	for(int i=MAX_BYTE+MAX_BYTE-1;i>=MAX_BYTE;i--)q.push(i);
	for(int i=MAX_BYTE-1;i;i--)
	
		tree[i].son[0]=q.top();q.pop();
		tree[i].son[1]=q.top();q.pop(); 
		cnt[i]=cnt[tree[i].son[0]]+cnt[tree[i].son[1]];
		q.push(i);
	


void read_data()

	memset(cnt,0,sizeof(cnt));
	FILE *infp=fopen(in_file_name,"rb");
	if(infp==NULL)
	
		printf("未找到待压缩文件\\n");
		exit(1);
	
    fseek(infp,0,SEEK_END);
	filesize=ftell(infp);
	fseek(infp,0,SEEK_SET);
	BYTE a;
	int b=0;
	system("cls");
	printf("文件总大小: %.3f KB\\n",(double)filesize/1024.0);
	printf("正在读取文件... %3d.%02d%%",b/100,b%100);
	for(int i=1;i<=filesize;i++)
	
	    fread(&a,sizeof(a),1,infp);
	    cnt[a+MAX_BYTE]++;
	    if(b==(ULL)i*10000/filesize)continue;
	    b=(ULL)i*10000/filesize;
	    for(int k=0;k<7;k++)putchar('\\b');
		printf("%3d.%02d%%",b/100,b%100);
	
	if(fclose(infp))
	
		printf("关闭输入文件失败\\n");
		exit(1);
	



以上是关于用哈弗曼编码实现文件压缩和解压(改进集成版1.0)的主要内容,如果未能解决你的问题,请参考以下文章

用哈弗曼编码实现文件压缩和解压

Java---Huffman树的实现

数据结构之哈弗曼编码的(Huffman Coding)加密解密压缩

经典解压缩软件 WinRAR 5.71 无广告版

解压过的解压文件可以删吗?

Jcompress: 一款基于huffman编码和最小堆的压缩解压缩小程序