文件壓縮與解壓

文件的壓縮與解壓

開發環境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 <string>
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<CharInfo> 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<CharInfo> h(_info, 256, invalid);
		HaffmanTreeNode<CharInfo>*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<CharInfo>*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<CharInfo>*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<class T>
struct  HaffmanTreeNode
{
	typedef HaffmanTreeNode<T> Node;

	T _weight;
	Node* _left;
	Node* _right;

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

template<class T>
class HaffmanTree
{
	typedef HaffmanTreeNode<T> 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<Node*,compare>  _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<vector>
#include<assert.h>
template<class T>
//比較器
class Less
{
public:
	bool operator() (const T& left, const T& right)
	{
		return left > right;
	}
};

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

template<class T, class Compare = Less<T> >
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<T>& 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<T> _arr; 
};

#endif //__Heap_H_

test.cpp中

#define _CRT_SECURE_NO_WARNINGS 1
#include<time.h>
#include <iostream>
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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章