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

放假了把这个改一下,发现确实用单字节压缩的压缩率要高一些,暂时没去管为什么,不过放假静下心来写的话确实效率高很多。

新版详见:http://blog.csdn.net/tookkke/article/details/50575103

今天脑洞大开突然想写一下,明明都要考试了,唉,怎么就管不住这手啊 

总之呢,就是根据每种编码的出现频率把等长的编码换成变长的,据说理论上压缩比率是比较高的,可是为什么经检验我这个大部分时候压缩出来的比源文件还大呢?

哈弗曼编码的时候要先做一颗字典树,查找的时候就按照当前一位是0还是1,找到叶子节点就找到了原编码。生成这颗树用一种贪心法,每次选两个出现频率最小的节点出来,连在一个新的节点上,再把该节点加入优先队列,它的频率是两个节点频率之和。

我这里把整棵生成的树都保存在了新文件中,然后同时保留的是新文件中内容转换成的变长编码总长度(bit 为单位,不能保证一定是8的倍数),还有把原编码看成二个字节一组,共65536种状态,因为不能保证字节数一定是偶数,所以新文件又用了二个字节保存它,多出来的就直接添加到新文件末尾了。

这是压缩程序,得到的文件名是原名+.kcps(随便写写也没那么多注释了)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

#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;

ULL cnt[MAX_WORD*2];//number of each WORD
WORD exBYTE=MAX_BYTE;//if exBYTE exists,it will less than MAX_BYTE
ULL bit_num;
FILE *infp,*outfp;
ULL kkke[MAX_WORD][2];

int tree_size=MAX_WORD*2;
struct the_tree{
	int son[2];
}tree[MAX_WORD*2];//root is 1

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

void open_file()
{
	char filename[80],outfilename[80];
	printf("please input the original file: ");
	scanf("%s",filename);
	strcpy(outfilename,filename);
	strcat(outfilename,".kcps");
	if((infp=fopen(filename,"rb"))==NULL)
	{
		printf("fail to open original file\n");
		exit(1);
	}
	if((outfp=fopen(outfilename,"wb"))==NULL)
	{
		printf("fail to create new file\n");
		exit(1);
	}
}

void close_file()
{
	if(fclose(infp))
	{
		printf("fail to close original file\n");
		exit(1);
	}
	if(fclose(outfp))
	{
		printf("fail to close new file\n");
	}
}

void read_data()
{
	int len;
	WORD a;
	while(len=fread(&a,sizeof(BYTE),sizeof(WORD)/sizeof(BYTE),infp))
	{
		if(len==1)
		{
			exBYTE=a%MAX_BYTE;
			break;
		}
		cnt[a+MAX_WORD]++;
	}
}

void build_tree()
{
	priority_queue<int,vector<int>,cmp>q;
	for(int i=MAX_WORD+MAX_WORD-1;i>=MAX_WORD;i--)q.push(i);
	for(int i=MAX_WORD-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 dfs(int k,ULL a,int cnt)
{
	if(k>=MAX_WORD)
	{
		k-=MAX_WORD;
		kkke[k][0]=a;
		kkke[k][1]=cnt;
	}
	else
	{
		dfs(tree[k].son[0],a<<1,cnt+1);
		dfs(tree[k].son[1],(a<<1)|1,cnt+1);
	}
}

/**********************/
/*****  tree      *****/
/*****  bit_num   *****/
/*****  cps       *****/
/*****  exBYTE    *****/
/**********************/

void output()
{
	for(int i=1;i<tree_size;i++)fwrite(&tree[i],sizeof(tree[i]),1,outfp);
	fseek(infp,0,SEEK_SET);
	WORD a;
	BYTE b=(BYTE)0;
	for(int i=0;i<MAX_WORD;i++)bit_num+=cnt[i+MAX_WORD]*kkke[i][1];
	fwrite(&bit_num,sizeof(bit_num),1,outfp);
	bit_num=0;
	while(fread(&a,sizeof(BYTE),sizeof(WORD)/sizeof(BYTE),infp)==sizeof(WORD)/sizeof(BYTE))
	{
		for(long long i=kkke[a][1]-1LL;i>=0LL;i--)
		{
			if(kkke[a][0]&(1ULL<<i))b+=(BYTE)1<<(bit_num%8);
			bit_num++;
			if(bit_num%8==0)
			{
				fwrite(&b,sizeof(b),1,outfp);
				b=0;
			}
		}
	}
	if(bit_num%8)fwrite(&b,sizeof(b),1,outfp);
	fwrite(&exBYTE,sizeof(exBYTE),1,outfp);
}

int main()
{
	open_file();
	read_data();
	build_tree();
	dfs(1,0ULL,0);
	output();
	close_file();
	return 0;
}

然后是解压程序,得到程序是(new)+原名,如果有.kcps就去掉

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

#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;

ULL cnt[MAX_WORD*2];//number of each WORD
WORD exBYTE=MAX_BYTE;//if exBYTE exists,it will less than MAX_BYTE
ULL bit_num;
FILE *infp,*outfp;
ULL kkke[MAX_WORD][2];

int tree_size=MAX_WORD*2;
struct the_tree{
	int son[2];
}tree[MAX_WORD*2];//root is 1

void open_file()
{
	char filename[80],outfilename[80];
	printf("please input the compressed file: ");
	scanf("%s",filename);
	if((infp=fopen(filename,"rb"))==NULL)
	{
		printf("fail to open compressed file\n");
		exit(1);
	}
	char *a=strstr(filename,".kcps");
	if(a!=NULL)*a='\0';
	strcpy(outfilename,"(new)");
	strcat(outfilename,filename);
	if((outfp=fopen(outfilename,"wb"))==NULL)
	{
		printf("fail to create new file\n");
		exit(1);
	}
}

void close_file()
{
	if(fclose(infp))
	{
		printf("fail to close compressed file\n");
		exit(1);
	}
	if(fclose(outfp))
	{
		printf("fail to close new file\n");
	}
}

/**********************/
/*****  tree      *****/
/*****  bit_num   *****/
/*****  cps       *****/
/*****  exBYTE    *****/
/**********************/

void read_data()
{
    for(int i=1;i<tree_size;i++)fread(&tree[i],sizeof(tree[i]),1,infp);
	fread(&bit_num,sizeof(bit_num),1,infp);
}

void output()
{
	BYTE a;
	WORD b;
	int now_node=1;
	for(int i=0;i<bit_num;i++)
	{
		if(i%8==0)fread(&a,sizeof(a),1,infp);
		if(a&((BYTE)1<<(i%8)))
		{
		    now_node=tree[now_node].son[1];
		}
		else
		{
		    now_node=tree[now_node].son[0];
		}
		if(now_node>=MAX_WORD)
		{
			b=now_node-MAX_WORD;
			fwrite(&b,sizeof(b),1,outfp);
			now_node=1;
		}
	}
	fread(&exBYTE,sizeof(exBYTE),1,infp);
	if(exBYTE<MAX_BYTE)fwrite(&exBYTE,sizeof(exBYTE),1,outfp);
}

int main()
{
	open_file();
	read_data();
	output();
	close_file();
	return 0;
}

然后是怨念,为什么要考试了我却还没复习的心情啊啊啊啊啊啊啊啊,为什么这个压缩程序实际上还把源文件放大了啊啊啊啊啊啊啊,难道只能当成加密程序用了吗???

高兴的是其实有一次压缩吧10000K压成了9000K,还是挺开心的,虽然只有一次...

时间: 2024-12-26 15:05:06

用哈弗曼编码实现文件压缩和解压的相关文章

34模块-zip【文件压缩和解压、图片压缩和编辑】

Zip模块管理文件压缩和解压,通过plus.zip可获取压缩管理对象. 比较常用的就是  对图片进行压缩.转码.旋转操作了 <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no&q

c#自带类实现的多文件压缩和解压

c#自带的System.IO.Compression命名空间下的压缩类实现的多文件压缩和解压功能,缺点是多文件压缩包的解压只能调用自身的解压方法,和现有的压缩软件不兼容.下面的代码没有把多文件的目录结构加进去 using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; namespace Test.Zip { class CompressHelper { /// <su

基于哈夫曼编码的文件压缩(c++版)

本博客由Rcchio原创 我了解到很多压缩文件的程序是基于哈夫曼编码来实现的,所以产生了自己用哈夫曼编码写一个压缩软件的想法,经过查阅资料和自己的思考,我用c++语言写出了该程序,并通过这篇文章来记录一下自己写该程序学到的东西.因为本人写的程序在压缩率上,还有提升的空间,所以本文将不定期更新,但程序整体的思路不会有较大的改动. 一.基于哈夫曼编码可实现压缩文件的原理分析 在计算机中,数据的存储都是二进制的,并且以字节作为基本的存储单位,像英文字母在文本中占一个字节,汉字占两个字节,我们把这种每一

.net文件压缩和解压及中文文件夹名称乱码问题

/**************************注释区域内为引用http://www.cnblogs.com/zhaozhan/archive/2012/05/28/2520701.html的博客内容 在.NET可以通过多种方式实现zip的压缩和解压:1.使用System.IO.Packaging:2.使用第三方类库:3.通过 System.IO.Compression 命名空间中新增的ZipArchive.ZipFile等类实现. 一.使用System.IO.Packaging压缩和解压

C# ICSharpCode.SharpZipLib.dll文件压缩和解压功能类整理,上传文件或下载文件很常用

工作中我们很多时候需要进行对文件进行压缩,比较通用的压缩的dll就是ICSharpCode.SharpZipLib.dll,废话不多了,网上也有很多的资料,我将其最常用的两个函数整理了一下,提供了一个通用的类,这样在工作中可以快速的完成压缩和解压缩的动作哦 官网下载地址:  http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx 1. 在项目中添加对ICSharpCode.SharpZipLib.dll的引用: 2. 在需要

python学习shutil模块的文件压缩和解压用法

shutil模块可以创建压缩包并返回文件路径,例如 zip,tar,下面详细其用法 base_name 压缩包的文件名,也可以是压缩包的路径,只是文件名时,则保存至当前目录,否则保存指定路径 data_bak 保存当前路径 format  压缩包种类  zip tar bztar gztar root_dir 要压缩的文件路径 owner 用户 group 用户组 logger 用于记录日志 1,压缩的用法 import zipfile z=zipfile.Zipfile('a.zip','w'

iOS网络-ZipArchive框架的文件压缩和解压

导入第三方框架ZipArchive之后还要在系统库文件中导入一个如下文件(搜索libz出来的任何一个都可以) 文件压缩 -(void)zip { NSArray *arrayM = @[@"/Users/gengqun/Desktop/Snip20160118_866.png", @"/Users/gengqun/Desktop/Snip20160118_867.png", @"/Users/gengqun/Desktop/Snip20160118_868

4_Linux_文件压缩和解压指令

3.4压缩解压命令.gz .tar.gz .zip .bz2 1)gzip 仅压缩文件 gzip命令用于压缩文件,英文原意为GNU zip,所在路径/bin/gzip,其语法格式为: gzip [文件] 压缩后的文件格式为.gz. 注: 1只能压缩文件 2不保留原文件 ? 2)gunzip 解压gunzip [压缩文件]或gzip –d [压缩文件] 3)tar 压缩文件和目录 tar命令用于打包目录,所在路径为/bin/tar,其语法格式为: tar 选项[-zcf] [压缩后的文件名] [目

Linux文件压缩和解压使用记录

一:tar(可压缩可解压) tar命令是Unix/Linux系统中备份文件的可靠方法,几乎可以工作于任何环境中,它的使用权限是所有用户.但是tar本身只是一个文件打包工具,只有和其他工具组合时才具有压缩/解压文件功能. 使用tar命令压缩文件的格式是:tar  参数[主选项+辅选项]   '文件或目录 ' 参数主选项 -c 创建新的档案文件.如果用户想备份一个目录或是一些文件,就要选择这个选项. -r 把要存档的文件追加到档案文件的未尾.例如用户已经做好备份文件,又发现还有一个目录或是一些文件忘