文件压缩与解压-创新互联

文件的压缩与解压

专注于为中小企业提供成都网站设计、成都网站建设服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业高明免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了1000+企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。

开发环境:vs2013

开发技术:vector、堆、哈夫曼树、文件部分函数的操作

项目描述:文件压缩是把一个占内存比较大的文件压缩成为一个占内存比较小的文件,节省了磁盘的空                间,传输时也比较方便。解压是把压缩的文件还原成原来的文件,读写比较方便。

                     √可以压缩与解压小文件,也可以压缩与解压大文件。

                  √ 在Debug下,压缩和解压6M左右文件,需要25s左右,在Release下,需要2s左右。

                     √ 有配置文件,压缩开发技术:vector、堆、哈夫曼树、文件部分函数的操作

遇到的问题:√ 在压缩过程中不够8位会补零,但在解压过程中会把零读出来,这样就不对了。为了解决                  这个问题。我在解压过程中定义了一个总长度,它写入一个字符,总长度就减1,当长度                  减为0,就不进去了。

                  √ 有时解压大文件时,字数不想等。于是我就把’w’换成了’wb’,把’r'换了’rb’因                     为用文本模式打开时,遇到’\n’,会多加’\r’,但用二进制模式打开就不会有这样的                     问题。

                  √ 有时读大文件时,会读不全。因此我就把EOF换成了feof。因为EOF是以-1为结束标志                       的,但文件中出现-1它就不会读下去了。如果换成feof的话,它是以“文件结束”为标                       志。这样就不会出现读不完的情况。

compress.h中

#ifndef __Compress_H_
#define __Compress_H_

#include 
typedef long long LongType;

struct CharInfo
{
	unsigned char _ch;//字符
	LongType _count;//出现次数
	string _code;//哈夫曼编码
	CharInfo(unsigned char ch = '0')
		:_ch(ch)
		, _count(0)
		, _code("")
	{}
	CharInfo(LongType count)
		:_count(count)
		, _ch(0)
		, _code("")
	{}
	//重载运算符“>”
	bool operator >(const CharInfo& com)const
	{
		return _count > com._count;
	}
	//重载运算符“!=”
	bool operator !=(CharInfo com)const 
	{
		return _count != com._count;
	}
	//重载运算符“+”
	CharInfo operator+(const CharInfo& com)const
	{
		return CharInfo(_count + com._count);
	}
};

class FileCompress
{
public:
	//压缩
	FileCompress()
	{
		for (size_t i = 0; i < 256; i++)
		{
			_info[i]._ch = i;
		}
	}
	void Compress(const char* file)
	{
		//读取文件
		FILE *fout = fopen(file, "rb");
		unsigned char ch = 0;
		assert(fout);
		//统计每个字符出现的次数
		ch = fgetc(fout);
		while (!feof(fout))
		{
			_info[(unsigned char)ch]._count++;
			ch = fgetc(fout);
		}
		//用次数建立哈夫曼树
		const CharInfo invalid((unsigned char)0);
		HaffmanTree tree(_info, 256,invalid);
		//生成哈夫曼编码
		string tmp;
		_CreateHaffmanCode(tree.GetRoot(), tmp);
		//压缩
		fseek(fout, 0, SEEK_SET);//从文件的首字符开始压缩
		//压缩后的文件名
		string comfile = file;
		comfile += ".haffman";
		FILE *fin = fopen(comfile.c_str(), "wb");
		assert(fin);
		unsigned char value = 0;
		size_t index = 0;//标记当前位
		ch = fgetc(fout);
		while (!feof(fout))
		{
			string code = _info[ch]._code;
			for (size_t i = 0; i < code.size(); i++)
			{
				if (code[i] == '1')
				{
					value <<= 1;
					value |= 1;
				}
				else
				{
					value <<= 1;
				}
				//满8个字节,将其写入到压缩文件中
				if (++index == 8)
				{
						fputc(value, fin);
					    value = 0;
					     index = 0;
				}
			}
			ch = fgetc(fout);
		}
		//如果写入完,value 没有写满8位,把剩余的压入压缩文件中
		if (index != 0)
		{
			value <<= (8 - index);
			fputc(value, fin);
		}
		//配置文件
		string configfile = file;
		configfile += ".config";
		FILE* finfo = fopen(configfile.c_str(), "wb");
		assert(finfo);
		//将字符的总个数写进配置文件
		char infostr[32];
		//将出现的字符以及次数写进配置文件
		string str;
		for (size_t j = 0; j < 256; j++)
		{
				if (_info[j]._count > 0)
				{
					str.push_back((unsigned char)j);
					str.push_back(',');
					_itoa(_info[j]._count, infostr, 10);
					str += infostr;
					fputs(str.c_str(), finfo);
					fputc('\n', finfo);
					str.clear();
				}
		}
		fclose(fout);
		fclose(fin);
		fclose(finfo);
	}
	//解压
	void UnCompress(const char *file)
	{
		unsigned char ch = 0;
		string fconfig = file;
		fconfig += ".config";
		//读压缩文件
		FILE* fout = fopen(fconfig.c_str(), "rb");
		assert(fout);
		string tmp;
		//字符出现的次数
		while (ReadLine(fout, tmp))
		{
			if (!tmp.empty())
			{
				//那到字符
				ch = tmp[0];
				//获取字符的次数
				_info[(unsigned char)ch]._count = atoi(tmp.substr(2).c_str());
				tmp.clear();
			}
			else
			{
				tmp = '\n';
			}
		}
		//重建哈夫曼树
		CharInfo invalid((unsigned char)0);
		HaffmanTree h(_info, 256, invalid);
		HaffmanTreeNode*root = h.GetRoot();
		//解压
		string output = file;
		output += ".uncom";
		FILE* fin = fopen(output.c_str(), "wb");
		assert(fin);
		//根据压缩文件,还原文件
		string Haffmanfile = file;
		Haffmanfile += ".haffman";
		FILE* fcom = fopen(Haffmanfile.c_str(), "rb");
		assert(fcom);
		HaffmanTreeNode*cur = root;
		ch = fgetc(fcom);
		int pos = 8;
		LongType len = root->_weight._count;
		while (len > 0)
		{
			while (cur->_left &&cur->_right)
			{
				if (pos == 0)
				{
					ch = fgetc(fcom);
					pos = 8;
				}
				--pos;
				//与1,走右树
				if (ch&(1 << pos))
				{
					cur = cur->_right;
				}
				//与0,走左树
				else
				{
					cur = cur->_left;
				}
			}
			if (cur)
			{
				fputc(cur->_weight._ch, fin);//写进解压文件
				cur = root;
			}
			--len;
		}
		fclose(fout);
		fclose(fin);
		fclose(fcom);
	}
protected:
	//创建哈夫曼编码
	void _CreateHaffmanCode(HaffmanTreeNode*root, string tmp)
	{
		//判空
		if (root == NULL)
		{
			return;
		}
		if (root->_left == NULL&&root->_right == NULL)
		{
			//找到下标,把编码写到_code中
			int index = (root->_weight)._ch;
			_info[index]._code = tmp;
		}
		else
		{
			//左树写0
			if (root->_left)
			{
				tmp.push_back('0');
				_CreateHaffmanCode(root->_left, tmp);
				tmp.pop_back();
			}
			//右树写1
			if (root->_right)
			{
				tmp.push_back('1');
				_CreateHaffmanCode(root->_right, tmp);
				tmp.pop_back();
			}
		}
	}
	//读一行
	bool ReadLine(FILE* file, string& tmp)
	{
		assert(file);
		char ch = fgetc(file);
		if (feof(file))
		{
			return false;
		}
		while (ch != '\n')
		{
			tmp += ch;
			ch = fgetc(file);
		}
		return true;
	}
protected:
	CharInfo _info[256];
};
#endif //__Compress_H_

HaffmanTree.h中

#ifndef __HaffmanTree_H_
#define __HaffmanTree_H_
#include "Heap.h"
template
struct  HaffmanTreeNode
{
	typedef HaffmanTreeNode Node;

	T _weight;
	Node* _left;
	Node* _right;

	HaffmanTreeNode(const T& weight)
		:_weight(weight)
		, _left(NULL)
		, _right(NULL)
	{}
};

template
class HaffmanTree
{
	typedef HaffmanTreeNode Node;
public:
	HaffmanTree()
		:_root(NULL)
	{}
	HaffmanTree(const T* arr, size_t size)
	{
		_root = _Create(arr, size);
	}
	//构造函数
	HaffmanTree(const T* arr, size_t size, const T& invalid)
	{ 
		_root = _Create(arr, size, invalid);
	}

	~HaffmanTree()
	{
		if (_root)
		{
			_Destroy(_root);
		}
	}

	Node* GetRoot()
	{
		return _root;
	}

protected:
	//创建哈夫曼树
	Node* _Create(const T* arr, size_t size, const T& invalid)
	{ 
		struct compare
		{
			bool operator ()(const Node *left, const Node *right)
			{
				return left->_weight > right->_weight;
			}
		};

		Heap  _heap;
		//把值放到vector中
		for (size_t i = 0; i < size; ++i)
		{
			if (arr[i] != invalid)
			{
				Node *tmp = new Node(arr[i]);
				_heap.Push(tmp);
			}
		}
		//构造哈夫曼树
		while (_heap.Size() > 1)
		{
			Node *left = _heap.Top();
			_heap.Pop();
			Node*right = _heap.Top();
			_heap.Pop();

			Node *parent = new Node(left->_weight + right->_weight);
			parent->_left = left;
			parent->_right = right;

			_heap.Push(parent);
		}

		return _heap.Top();
	}
	//释放节点
	void _Destroy(Node* root)
	{ 
		if (root->_left == NULL && root->_right == NULL)
		{
			delete root;
			root = NULL;
		}
		else
		{
			_Destroy(root->_left);
			_Destroy(root->_right);
		}
	}

private:
	Node *_root;
};
#endif //__HaffmanTree_H_

Heap.h中

#ifndef __Heap_H_
#define __Heap_H_

#include
#include
template
//比较器
class Less
{
public:
	bool operator() (const T& left, const T& right)
	{
		return left > right;
	}
};

template
class Greater
{
public:
	bool operator() (const T& left, const T& right)
	{
		return left < right;
	}
};

template >
class Heap
{
public:
	//构造函数
	Heap()
		:_arr(NULL)
	{}
	Heap(const T* arr, int size)
	{
		assert(arr);
		_arr.reserve(size);

		for (int i = 0; i < size; i++)
		{
			_arr.push_back(arr[i]);
		}

		int begin = _arr.size() / 2 - 1;
		while (begin >= 0)
		{
			_AdjustDown(begin);
			begin--;         
		}
	}
	//拷贝构造
	Heap(vector& s)
	{
		_arr = s;
		int begin = _arr.size() / 2 - 1;
		while (begin >= 0)
		{
			_AdjustDown(begin);
			begin--;            
		}
	}
	void Push(const T& x)
	{
		_arr.push_back(x);
		int begin = _arr.size();
		_AdjustUp(begin);
	}

	void Pop() 
	{
		_arr[0] = _arr[_arr.size() - 1];
		_arr.pop_back();
		_AdjustDown(0);
	}

	void Clear()
	{
		_arr.clear();
	}

	bool Empty()
	{
		return _arr.empty();
	}

  T& Top() 
	{
		if (!Empty())
		{
			return _arr[0];
		}
		exit(1);
	}

	size_t Size()
	{
		return _arr.size();
	}

protected:
	//下调
	void _AdjustDown(int parent)
	{ // 从根节点向下调整
		int size = _arr.size();
		int left = parent * 2 + 1;
		int right = left + 1;
		int key = left;
		while (left < size)
		{//Compare()仿函数
			//if (right < size && array[left] > array[right])
			if (right < size && Compare()(_arr[left], _arr[right]))  //右边小
			{
				key = right;
			}
			else
			{
				key = left;
			}
			//if ((array[key] > array[parent]))
			if (Compare()(_arr[key], _arr[parent]))
			{
				break;
			}
			//else if (array[parent] > array[key])
			else if (Compare()(_arr[parent], _arr[key])) //左边小
			{
				swap(_arr[key], _arr[parent]);
			}

			parent = key;
			left = parent * 2 + 1;
			right = left + 1;
		}
	}
	//上调
	void _AdjustUp(int child)
	{  //从叶子节点或子节点向上调整
		int size = _arr.size();
		if (size == 0 || size == 1)
		{
			return;
		}

		int parent = (child - 2) / 2;
		int left = parent * 2 + 1;
		int right = left + 1;
		int key = 0;
		while (parent >= 0)
		{
			//if (right < size && array[left] > array[right])
			if (right < size && Compare()(_arr[left], _arr[right]))
			{
				key = right;
			}
			else
			{
				key = left;
			}
			//if (array[parent] > array[key])
			if (Compare()(_arr[parent], _arr[key]))
			{
				swap(_arr[parent], _arr[key]);
			}
			else
			{
				break;
			}

			if (parent == 0)
			{
				break;
			}

			parent = (parent - 1) / 2;
			left = parent * 2 + 1;
			right = left + 1;
		}
	}

private:
	vector _arr; 
};

#endif //__Heap_H_

test.cpp中

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include 
using namespace std;

#include "HaffmanTree.h"
#include "Compress.h"
void Test()
{
	double t1;
	double t2;
	FileCompress f;
	//f.Compress("input.txt");
	f.Compress("mnt.txt");
	t1 = clock();
	printf("压缩文件需要的时间: %f s \n", t1 / CLOCKS_PER_SEC);
	//f.UnCompress("input.txt");
	f.UnCompress("mnt.txt");
	t2 = clock();
	printf("解压缩文件需要的时间: %f s \n", t2 / CLOCKS_PER_SEC);
}
int main()
{
	Test();
	system("pause");
	return 0;
}

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


分享名称:文件压缩与解压-创新互联
本文路径:http://hbruida.cn/article/dipejp.html