文件压缩(小项目)

简介:利用哈夫曼树实现一个文本文档的压缩,以及对压缩文件的解压

思路:在压缩文件时,首先要统计字符出现的次数,构建哈夫曼树,生成哈夫曼编码,压缩到文件。

在解压文件时,读取压缩文件,将编码与字符相对应,最后将字符写到文件中。

在解压文件中,如何将编码与字符相对应?

我们都知道,在解压文件时,我们只有一个压缩文件,其余一慨不知。所以在解压时,需要重建哈夫曼树。要想重建哈夫曼树,就需要知道字符以及字符出现的次数。在压缩文件时,已经统计出字符出现的次数。所以,在压缩文件时,应该写配置文件。配置文件中存放字符以及字符出现的次数。在解压时,读取压缩文件,配置文件,重建哈夫曼树,将编码与字符相对应。

//建堆  
#pragma once
#include <iostream>
#include <vector>
using namespace std;

template <class T>
struct Less  //小于
{
	bool operator()(const T& l,const T& r)
	{
		return l < r;
	}
};

template <class T>
struct Greater  //大于
{
	bool operator()(const T& l,const T& r)
	{
		return l > r;
	}
};

template <class T,class Comper = Greater<T> >//默认建大堆
class Heap
{
public:
	Heap()   //无参构造函数
	{}

	Heap(T* a,size_t size)
	{
		for(size_t i=0;i<size;++i)
		{
			_a.push_back(a[i]);
		}
		//建堆
		for(int i=(_a.size()-2)/2;i>=0;--i)
		{
			_ApDown(i);
		}
	}

	void Push(const T& x)//插入元素
	{
		_a.push_back(x);//在堆尾插入元素
		_ApHeapUp(_a.size()-1);	//向上调整
	}

	void Pop()//删除(删除优先级高)
	{
		swap(_a[0],_a[_a.size()-1]);//交换堆的第一个元素和最后一个元素
		_a.pop_back();//删除最后一个元素
		_ApDown(0);//向下调整
	}

	size_t Size()//堆的大小
   	{
		return _a.size();
	}

	bool Empty()//堆是否为空
	{
		return _a.empty();
	}

	T Top()
	{
		return _a[0];
	}

public:
	void _ApDown(size_t parent) 
	{
		size_t child = parent*2+1;

		while(child < _a.size())
		{
			Comper com;
			//找到左右孩子中较大的
			if((child+1) < _a.size() && com(_a[child+1],_a[child]))
			{
				++child;
			}
			//比较较大孩子与父亲
			if(com(_a[child],_a[parent]))
			{
				swap(_a[child],_a[parent]);
				parent = child;
				child = parent*2+1;
			}
			else
			{
				break;
			}
		}
	}

	void _ApHeapUp(size_t child)
	{
		size_t parent = (child-1)/2;
		Comper com;
		while(child > 0)
		{
			if(com(_a[child],_a[parent]))//比较孩子与父亲
			{
				swap(_a[child],_a[parent]);
				child = parent;
				parent = (child-1)/2;
			}
			else
			{
				break;
			}
		}
	}

protected:
	vector<T> _a;
};
//建哈夫曼树 Huffman.h
#include "Heap.h"

template <class T>
struct HuffmanTreeNode
{
	HuffmanTreeNode(const T& x)
		:_left(NULL)
		,_right(NULL)
		,_weight(x)
	{}
	HuffmanTreeNode<T>* _left;
	HuffmanTreeNode<T>* _right;
	T _weight;
};

template <class T>
class HuffmanTree
{
	typedef HuffmanTreeNode<T> Node; 
public:
	HuffmanTree(const T* a,size_t n,const T& invalue)
	{
		struct IsLess
		{
			bool operator()(const Node* left,const Node* right)
			{
				return left->_weight < right->_weight;
			}
		};
		Heap<Node*,IsLess> minHeap;
		for(size_t i=0;i<n;++i)
		{
			if(a[i] != invalue)
			{
				minHeap.Push(new Node(a[i])); //建小堆
			}
		}
		while(minHeap.Size() > 1)
		{
			Node* left = minHeap.Top();
			minHeap.Pop();
			Node* right = minHeap.Top();
			minHeap.Pop();
			Node* parent = new Node(left->_weight+right->_weight);
			parent->_left = left;
			parent->_right = right;
			minHeap.Push(parent);
		}
		_root = minHeap.Top();
	}
	Node* GetRoot()
	{
		return _root;
	}
protected:
	Node* _root;
};

void HuffmanTreeTest()
{
	int a[] = {1,2,3,4,5,6,7,8,9};
	HuffmanTree<int> ht(a,sizeof(a)/sizeof(a[0]),‘#‘);
}
//实现压缩,解压  FileCompare.h
#define  _CRT_SECURE_NO_WARNINGS
#include "HuffmanTree.h"
#include <assert.h>
#include <string>
#include <stdlib.h>
typedef unsigned long LongType;

struct CharInfo
{
	unsigned char _ch;    //字符
	LongType _count;      //字符出现的次数
	string _code;         //字符对应的Huffman编码

	CharInfo()
		:_ch(0)
		,_count(0)
	{}

	CharInfo(LongType count)
		:_ch(0)
		,_count(count)
	{}

	bool operator!=(const CharInfo& info) const
	{
		return _count != info._count;
	}
	CharInfo operator+(const CharInfo& info) const
	{
		return CharInfo(_count + info._count);
	}
	bool operator<(const CharInfo& info) const
	{
		return _count < info._count;
	}
};

class FileCompress
{
public:
	FileCompress()
	{
		for(size_t i=0;i<256;++i)
		{
			_info[i]._ch = i;
			_info[i]._count = 0;
		}

	}
	void GetHuffmanCode(HuffmanTreeNode<CharInfo>* root,string code)//获取哈夫曼编码
	{
		if(root == NULL)
			return;

		if(root->_left == NULL && root->_right == NULL)
		{
			 _info[root->_weight._ch]._code = code;
		}

		GetHuffmanCode(root->_left,code + ‘0‘);//左为0
		GetHuffmanCode(root->_right,code + ‘1‘);//右为1
	}

	bool ReadLine(FILE* fout,string& line)
	{
		char ch = fgetc(fout);
		if(feof(fout))   //若结束返回非零值
			return false;
		while(!feof(fout) && ch != ‘\n‘)
		{
			line += ch;
			ch = fgetc(fout);
		}
		return true;
	}

	void Compress(const char* filename)
	{
	    //统计字符的次数
		FILE* fout = fopen(filename,"rb");
		assert(fout);
		char ch = fgetc(fout);
		while(!feof(fout))   //读到文件尾的标志位   若采用ch != EOF   11111111 跳出读取文件
		{
			_info[(unsigned char)ch]._count++;
			ch = fgetc(fout);
		}
		//构建Huffman树
		CharInfo invalue;  //非法值
		HuffmanTree<CharInfo> tree(_info,256,invalue);

		//生成Huffman编码
		string code;
		GetHuffmanCode(tree.GetRoot(),code);

		//压缩
		string comFilename = filename;
		comFilename += ".compress";
		FILE* fin = fopen(comFilename.c_str(),"wb");

		assert(fin);
		fseek(fout,0,SEEK_SET);  //设置文件指针的位置

		ch = fgetc(fout);
		int size = 0;
		int value = 0;
		while(!feof(fout))  //feof 来判断文件是否执行结束,若结束,则返回非零值。
		{
			string code = _info[(unsigned char)ch]._code;
			for(size_t i=0;i<code.size();++i)
			{
				if(code[i] == ‘1‘)
				{
					value |= 1;
				}
				++size;
				if(size == 8)
				{
					fputc(value,fin);
					size = 0;
					value = 0;
				}
				value <<= 1;
			}
			ch = fgetc(fout);
		}
		if(size > 0)
		{
			value <<= (7-size);
			fputc(value,fin);
		}

		//配置文件
		string configfile = filename;
		configfile += ".config";
		FILE* fconfig = fopen(configfile.c_str(),"wb");//以二进制的形式打开
		assert(fconfig);

		char buffer[256];
		string line;
		for(size_t i=0;i<256;++i)
		{
			if(_info[i]._count > 0)
			{
				line += _info[i]._ch;
				line += ‘,‘;
				line += itoa(_info[i]._count,buffer,10);
				line += ‘\n‘;
				fputs(line.c_str(),fconfig);
			}
			line.clear();
		}
		fclose(fout);
		fclose(fin);
		fclose(fconfig);
	}
	void Uncompress(const char* filename)
	{
		//读配置文件
		string configfile = filename;
		configfile += ".config";
		FILE* fconfig = fopen(configfile.c_str(),"rb");//以二进制的形式读取
		assert(fconfig);
		string str;
		while(ReadLine(fconfig,str))
		{
			if(str.empty())  //处理空行
			{
				str += ‘\n‘;
			}
			else
			{
				_info[(unsigned char)str[0]]._count = atoi(str.substr(2).c_str());//第二个位置即第三个字符为字符的次数
				str.clear();
			}
		}

		//构建Huffman树
		CharInfo invalue;
		HuffmanTree<CharInfo> tree(_info,256,invalue);

		//读取压缩文件,进行还原
		string comFilename = filename;
		comFilename += ".compress";

		FILE* fout = fopen(comFilename.c_str(),"rb");

		assert(fout);

		HuffmanTreeNode<CharInfo>* root = tree.GetRoot();
		HuffmanTreeNode<CharInfo>* cur = root;
		string uncomFilename = filename;
		uncomFilename += ".uncompress";
		FILE* fin = fopen(uncomFilename.c_str(),"wb");
		assert(fin);
		LongType SumCount = tree.GetRoot()->_weight._count; //总数

		char ch = fgetc(fout);
		int pos = 7;

		while(1)
		{
			if(ch & (1<<pos))
			{
				cur = cur->_right;
			}
			else
			{
				cur = cur->_left;
			}
			if(cur->_left == NULL && cur->_right == NULL)
			{
				fputc(cur->_weight._ch,fin);
				if(--SumCount == 0)
				{
					break;
				}
				cur = root;
			}
			if(pos-- == 0)
			{
				ch = fgetc(fout);
				pos = 7;
			}
		}
		fclose(fout);
		fclose(fin);
	}
protected:
	CharInfo _info[256];
};

void PressHuffmanTest()
{
	FileCompress fh;
	fh.Compress("input");
	//fh.Compress("project.txt");

}

void UnPressHuffmanTest()
{
	FileCompress fh;
	fh.Uncompress("input");
	//fh.Uncompress("project.txt");
}
//测试
#include "FileCompree.h"
#include <windows.h>

int main()
{
	//HuffmanTreeTest(); //验证哈弗曼树
	int begin1 = GetTickCount();
	PressHuffmanTest();
	int end1 = GetTickCount();
	cout<<"压缩时间为:"<<end1-begin1<<endl;

	int begin2 = GetTickCount();
	UnPressHuffmanTest();
	int end2 = GetTickCount();
	cout<<"解压时间为:"<<end2-begin2<<endl;
	return 0;
}

测试结果:

比较结果:

时间: 2024-07-31 10:11:22

文件压缩(小项目)的相关文章

解析Job,bpmn文件的小项目总结

1.在使用String类中split(String regex)切割字符串abcd.job遇得到job字符串时,直接使用split("."),导致数组超出界限错误 原因:得到的数组长度为0. 结论:对于像 . ,*,|这些特殊的字符作为切割界限符时要加转义字符,如:"\\.","\\|". 2.在Job文件中,对于流程-待办,待办-变量两者都是一对多的关系, 在fn-xte中,项目表示Job文件数据的方式是,String-List<Map&

小代码 项目(3)M 文件压缩

 <html> <HEAD></HEAD> <BODY>   <textarea rows="10" cols="50"> </textarea>      <FONT style="FONT-SIZE: 120pt; COLOR: green; FONT-FAMILY: Webdings">û</FONT> <textarea rows=&qu

把音频文件压缩变小的方法

有时候一个音频文件比较大,传输以及使用很不方便,需要把文件压缩变小,这里就提供一种压缩的工具. 注意,压缩音频文件是以降低码率为代价的,可能会影响音频效果 码率计算公式 基本的算法是:[码率](kbps)=[文件大小](字节)X8/[时间](秒)/1000 音频文件专用算法:[比特率](kbps)=[量化采样点](kHz)×[位深](bit/采样点)×[声道数量](一般为2) 软件准备 酷狗音乐 开始压缩 1.准备好一个需要压缩的音频文件. 查看属性可以看到,这个文件有200多M 2.打开酷狗音

Java小项目之:文件的加密与解密!再也不怕存的小电影被别人发现了!

Java小项目之:文件的加密与解密!再也不怕存的小电影被别人发现了!今天带来的java小项目是加密解密系统,再也不怕别人偷看自己的电脑了,也可以正大光明的存小电影了.减少借别人电脑被看隐私的尴尬,从这个项目开始!界面展示: 部分代码展示:package wt.diy.encryption.gui; import java.io.File; import javax.swing.JFileChooser;import javax.swing.JFrame;import javax.swing.JO

视频压缩软件怎么将文件变小?教你快速压缩的方法

视频压缩软件怎么将文件变小?随着制作视频的小伙伴越来越多,大家都想把制作好的视频上传到一些平台分享自己的作品.但是我们在上传视频的时候经常会因为视频文件过大而导致文件不能正常上传,因此我们要用视频压缩软件将文件变小.那我们该怎么使用视频压缩软件压缩视频呢?下面分享一个搞定压缩视频的方法. 视频压缩软件https://www.xunjieshipin.com/download-converter 压缩方法:首先在电脑上安装一个可以将视频文件变小的视频压缩软件,看到初始页面有视频转换和视频分割等九种

Huffman的应用之文件压缩与解压缩

文件压缩与解压缩> 最近这段时间一直在学习树的这种数据结构,也接触到了Huffman树以及了解了什仫是Huffman编码,而我们常用的zip压缩也是利用的Huffman编码的特性,那仫是不是可以自己实现一个文件压缩呢?当然可以了.在文件压缩中我实现了Huffman树和建堆Heap的代码,zip压缩的介绍> http://www.cricode.com/3481.html 下面开始介绍自己实现的文件压缩的思路和问题... 1).统计>读取一个文件统计这个文件中字符出现的次数. 2).建树&

小项目创意大集合

每个程序员都可以入手的小项目创意大集合 我经常看有人发帖问关于软件项目创意点子的事,也看到了很多回帖,我自己也回了一些常见的软件项目创意.不过我觉得只列出三两个是远远不够的,因此就收集并这个软件项目创意列表,大家要找简单的编程软件项目创意学习练手的话,可以收藏并扩散本文.这些软件项目创意并不是论文级别的,只是想抛砖引玉让大家能从中受些启发. 下面你们会看到 120 多个个软件项目创意想法,都是我通过头脑风暴得来的.我将其根据主题分成了10 个分类,但有些软件项目创意其实涵盖了不止一个主题. 更新

开源作品-PHP写的JS和CSS文件压缩利器-SuMinify_PHP_1_5

前言: 网站项目需要引用外部文件以减小加载流量,而且第一次加载外部资源文件后,其他同域名的页面如果引用相同的地址,可以利用浏览器缓存直接读取本地缓存资源文件,而不需要每个页面都下载相同的外部资源文件.外部资源文件有一些是第三方插件,有一些是自己写的业务功能脚本,项目引用这些外部文件,需要把没有压缩过的第三方插件压缩后再引用,而自己的业务功能脚本,有的做成插件形式,引用一个功能脚本,需要同时引用若干个脚本文件,因此需要把这些脚本文件合并成一个压缩文件再引用,这样可以减少HTTP请求次数,减少这些外

用Python写一个带图形界面的文件压缩软件

文件压缩和解压我们在日常工作学习中会经常用到,比如winrar.快压.好压等压缩软件 打开之后的界面长这个样子: 压缩完成后是这个样子: 解压完成后是这个样子: 大家在学python的时候肯定会遇到很多难题,以及对于新技术的追求,这里推荐一下我们的Python学习扣qun:784758214,这里是python学习者聚集地!!同时,自己是一名高级python开发工程师,从基础的python脚本到web开发.爬虫.django.数据挖掘等,零基础到项目实战的资料都有整理.送给每一位python的小

Android开发不得不看的11个实战小项目

是不是想学Android开发(http://www.maiziedu.com/course/android-px/)却不知道如何下手?懂得一点点入门基础知识却无法应用到实际开发中?看相关资料觉得都懂了实际动手却发现什么都不懂?本地搭建Android开发环境太麻烦? 如果你有以上的各种问题,那么今天小编推荐的Android开发的这11个小项目,你一定要看!! 因为,这些实战项目都是基于google 官方的API Demos制作而成,而且全部配有Android在线开发环境,你可以随时动手跟着课程操作