1. 程式人生 > >【c++】Huffman實現檔案壓縮

【c++】Huffman實現檔案壓縮

1.需求分析

利用小堆,huffman編碼,檔案流操作,二進位制檔案的讀寫實現對普通檔案的壓縮和解壓過程。

2.能力要求

A.熟悉對檔案的讀寫操作。

B.熟悉小堆的原理。

C.熟悉HuffmanTree的實現原理、

D.會編碼的獲取。

E.對編碼資訊處理和儲存。

F.最重要一點,要理解壓縮和解壓縮的過程。

3.模組介紹。

A設計的儲存結構。.

B.壓縮過程.

C.解壓縮過程。

D.編碼的獲取。.

E.HuffmanTree的實現。

F.小堆的實現。

G.讀檔案的操作。

4.壓縮和解壓縮的原理。

壓縮過程:利用資料結構,對檔案裡面的字元進行統計。以字元出現的頻率構建小堆,再利用小堆,構建HuffmanTree。以

HuffmanTree左右孩子遍歷,左為0,右為1.將統計出來的結果存放到自己設計的結構中。在通過對字元編碼進行位進位制壓縮,可以實現壓縮過程。

解壓縮過程:利用字元和統計次數寫配置檔案,解壓縮就可以通過配置檔案建立一顆HuffmanTree,在根據壓縮檔案內容遍歷HuffmanTree,葉子節點即為原來的一個字元。

5.模組具體分析。

A設計的儲存結構。

typedef long longtype;
struct FileInfo
{
unsigned char _ch;           //字元
longtype _count;                 //計數器
string _code;               //Huffman編碼

}

B.壓縮過程.

1.先把檔案內容讀進來,並進行字元統計。

2.統計字元出現字數,存入_count。

3.依據每個節點的_count值,構建相應的huffman樹。

4.將編碼壓縮,存入檔案。

5.把字元和出現次數按照(字元,次數)的形式,每行的儲存到配置檔案裡。

C.解壓縮過程。

1.讀取配置檔案裡的內容,構建HuffmanTree。

2.根據壓縮檔案,和重建的huffman樹解壓.

3.根據壓縮檔案字元編碼解壓檔案.

D.編碼的獲取

void huffmancode(HuffManNode<FileInfo>* root, string& code)

E.HuffmanTree的實現。

struct HuffManNode
{
HuffManNode<T> *_parent;
HuffManNode<T> *_right;
HuffManNode<T> *_left;


T _weight;

}

class HuffManTree
{
public:
typedef HuffManNode<T> Node;

//仿函式
template<class T>
struct Compare
{
bool operator()(Node*& L, Node*& R)
{
return L->_weight < R->_weight;
}
};

private:
Node* _root;

F.小堆的實現。

Heap(const T* array, size_t size)
{
_array.reserve(size);
for (size_t i = 0; i < size; ++i)
{
_array.push_back(array[i]);
}


for (int begin = _array.size() / 2 - 1;
begin >= 0; begin--)
{
_AdjustDown(begin);
}
}

G.讀檔案的操作。

bool readline(FILE* str, string& code)

小堆程式碼:

#pragma once

#include <vector>
#include <algorithm>

template<class T>
class Less
{
public:
	bool operator()(const T& l, const T& r)
	{
		return l < r;
	}
};

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

// 小堆
//template<class T, class Compare>
template<class T, template<class> class Compare = Less>
class Heap
{
public:
	Heap()
	{}

	Heap(const T* array, size_t size)
	{
		_array.reserve(size);
		for (size_t i = 0; i < size; ++i)
		{
			_array.push_back(array[i]);
		}

		for (int begin = _array.size() / 2 - 1;
			begin >= 0; begin--)
		{
			_AdjustDown(begin);
		}
	}

	Heap(vector<T>& array)
	{
		_array.swap(array);

		for (int begin = _array.size() / 2 - 1;
			begin >= 0; begin--)
		{
			_AdjustDown(begin);
		}
	}


	void Push(const T& x)
	{
		_array.push_back(x);

		//?
		_AdjustUp(_array.size() - 1);
	}

	void Pop()
	{
		_array[0] = _array[_array.size() - 1];
		_array.pop_back();

		_AdjustDown(0);
	}

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

	const T& Top()
	{
		return _array[0];
	}

protected:
	void _AdjustDown(int root)//size
	{
		int left = root * 2 + 1;
		int right = left + 1;

		// 左孩子已不存在,則root為葉子節點,不用再調整
		while (left < _array.size())
		{
			// 找左右孩子裡面小的那個
			int key = left;
			if (right < _array.size()
				&& Compare<T>()(_array[right], _array[left]))
			{
				key = right;
			}

			// 如果min小則跟根節點交換
			//Compare<T> com;
			//if (_array[min] < _array[root])
			if (Compare<T>()(_array[key], _array[root]))
			{
				swap(_array[key], _array[root]);
				root = key;
				left = 2 * root + 1;
				right = left + 1;
			}
			else
			{
				break;
			}
		}
	}

	void _AdjustUp(int child)
	{
		while (1)
		{
			int root = (child - 1) / 2;
			//if (_array[root] > _array[child])
			if (Compare<T>()(_array[root], _array[child]))
			{
				swap(_array[root], _array[child]);
				child = root;
			}
			else
			{
				break;
			}

			if (root == 0)
				break;
		}
	}

private:
	vector<T> _array;
};

HuffmanTree程式碼:
#pragma once
#include"heap.h"

template <class T>

struct HuffManNode
{
	HuffManNode<T> *_parent;
	HuffManNode<T> *_right;
	HuffManNode<T> *_left;

	T _weight;

	HuffManNode(T weight)
		: _parent(NULL)
		, _right(NULL)
		, _left(NULL)
		, _weight(weight)
	{
	}
};

template <class T>
class HuffManTree
{
public:
	typedef HuffManNode<T> Node;


	//仿函式
	template<class T>
	struct Compare
	{
		bool operator()(Node*& L, Node*& R)
		{
			return L->_weight < R->_weight;
		}
	};

public:

	void CreatHuffmanTree(const T* array, size_t size, const T& invalid)
	{
		_CreatHuffmanTree(array, size, invalid);
	}
	HuffManTree()
		:_root(NULL)
	{
	}

	~HuffManTree()
	{
		_Destory(_root);
	}

	Node* GetRoot()
	{
		return _root;
	}



protected:

	void _CreatHuffmanTree(const T* array, size_t size, const T& invalid)
	{
		//將資料存到最小堆中(構建結點)
		Heap<Node*, Compare<T>> H;
		for (int i = 0; i < size; i++)
		{
			if (array[i] != invalid)
			{
				Node* node = new Node(array[i]);
				H.Push(node);
			}
		}

		//取堆中的最小兩個值,作為節點構建Huffman樹。
		Node* parent = H.Top();
		while (H.Size() > 1)
		{
			Node* left = H.Top();
			H.Pop();
			Node* right = H.Top();
			H.Pop();

			parent = new Node(left->_weight + right->_weight);
			//構建父節點
			parent->_left = left;
			parent->_right = right;
			left->_parent = parent;
			right->_parent = parent;

			H.Push(parent);
		}
		_root = parent;
	}

	bool _Destory(Node*& root)
	{
		if (root)
		{
			_Destory(root->_left);
			_Destory(root->_right);
			delete root;
			root = NULL;
		}
		return true;
	}
private:
	Node* _root;
};

檔案壓縮實現:
#pragma once
//#include"HuffMan.h"
//#include<string>
//#include<assert.h>

typedef long longtype;
struct FileInfo
{
	unsigned char _ch;           //字元
	longtype _count;                 //計數器
	string _code;               //Huffman編碼

	FileInfo(longtype count = 0)
		:_ch(0)
		, _count(count)
		, _code(NULL)
	{}

	FileInfo operator+(const FileInfo& info) const
	{
		FileInfo tmp;
		tmp._count = _count + info._count;
		return tmp;
	}

	bool operator!=(const FileInfo& info) const
	{
		return _count != info._count;
	}

	bool operator<(const FileInfo& info) const
	{
		return _count < info._count;
	}

};
ostream& operator<<(ostream& os, const FileInfo& info)
{
	os << info._ch << ":" << info._count;
	return os;
}
class FileCompress
{
public:
	FileCompress()
	{
		cout << "aaaaa" << endl;
		/*for (int i = 0; i < 256; ++i)
		{
				_info[i]._ch = i;
				_info[i]._count = 0;
		}*/
	}

	void huffmancode(HuffManNode<FileInfo>* root, string& code)
	{
		if (root == NULL)
		{
			return;// root->_right;
		}
		huffmancode(root->_left, code + '0');
		huffmancode(root->_right, code + '1');
		if (root->_left == NULL&&root->_right == NULL)
		{
			_info[root->_weight._ch]._code = code;   //生成並儲存huffman編碼
		}
		code.empty();//清空上次所存資料,為下次做準備
	}


	//檔案壓縮
	void filecompress(const char* filename)
	{
		//1.先把檔案內容讀進來,並進行字元統計
		FILE* read = fopen(filename, "rb");
		if (read == NULL)
		{
			return;
		}
		char ch = fgetc(read);

		//2.統計字元出現字數,存入_count
		while (ch != EOF)
		{
			_info[(unsigned char)ch]._count++;//
			ch = fgetc(read);
		}

		//3.依據每個節點的_count值,構建相應的huffman樹
		HuffManTree<FileInfo> tree;
		FileInfo invalid(0);
		tree.CreatHuffmanTree(_info, 256, invalid);

		string code;
		huffmancode(tree.GetRoot(), code);
		//4.將編碼壓縮,存入檔案

		string compressfile = filename;
		compressfile += ".compress";
		FILE* fout = fopen(compressfile.c_str(), "wb");
		assert(fout);

		fseek(fout, 0, SEEK_SET);//fout檔案指標,0位數,seek_set表示檔案開頭
		int index = 0;
		unsigned char comch = 0;
		ch = fgetc(read);
		while (ch != EOF)
		{
			string& code = _info[(unsigned char)ch]._code;

			//位進位制壓縮
			for (int i = 0; i < 256; i++)
			{
				comch << 1;
				if (code[i] == '1')
				{
					comch |= 1;
				}

				if (++index == 8)
				{
					fputc(comch, fout);
					index = 0;
					comch = 0;
				}
			}
			ch = fgetc(read);
		}

		if (index != 0)
		{
			comch << (8 - index);
			fputc(comch, fout);
		}


		//5.把字元和出現次數按照(字元,次數)的形式,每行的儲存到配置檔案裡
		string configfile = filename;
		configfile = ".config";
		FILE* config = fopen(configfile.c_str(), "w");
		assert(config);

		string info;
		char count[20];
		for (int i = 0; i < 256; i++)
		{
			info.clear();
			if (_info[i] != invalid)
			{
				info = _info[i]._ch;
				info += ',';
				itoa(_info[ch]._count, count, 10);

				info += count;
				info += '\n';
				fputs(info.c_str(), config);

			}
		}
		fclose(read);
		fclose(fout);//壓縮
		fclose(config);//配置

	}
	void uncompressfile(const char* filename)
	{
		//讀取配置檔案裡的內容
		string unfile = filename;
		unfile += ".comm";
		FILE* file = fopen(unfile.c_str(), "rb");
		assert(file);

		string code;
		char ch = 0;
		while (readline(file, code))
		{
			//若讀到空行,則為‘\0’
			if (!code.empty())
			{
				ch = code[0];
				_info[(unsigned char)ch]._count = atoi(code.substr(2).c_str());
				code.clear();
			}
			else
			{
				code = '\n';
			}
		}

		//根據配置檔案構建huffman樹
		HuffManTree<FileInfo> tree;
		FileInfo invalid(0);
		tree.CreatHuffmanTree(_info, 256, invalid);

		HuffManNode<FileInfo>* root = tree.GetRoot();

		//根據壓縮檔案,和重建的huffman樹解壓
		string  uncompress = filename;
		uncompress += ".uncompress";
		FILE* fin = fopen(uncompress.c_str(), "wb");
		assert(fin);

		string  compress = filename;
		compress += ".compress";
		FILE* fout = fopen(compress.c_str(), "rb");
		assert(fout);

		//根據壓縮檔案字元編碼解壓檔案
		HuffManNode<FileInfo>* str = root;
		int pos = 8;
		ch = fgetc(fin);
		assert(ch);

		longtype count = root->_weight._count;

		while (1)
		{
			if (pos == 0)
			{
				pos = 8;
				ch = fgetc(fin);
			}
			--pos;
			if (ch & 1 << pos)
				str = str->_right;
			else
				str = str->_left;


			if (str->_left == NULL && str->_right == NULL)
			{
				fputc(str->_weight._ch, fout);
				str = root;

				if (--count == 0)
					break;
			}
		}
		fclose(fout);
		fclose(fin);
	}


	bool readline(FILE* str, string& code)
	{
		assert(str);
		char ch = fgetc(str);
		if (ch == EOF)
		{
			return false;
		}

		while (ch != '\n'&&ch != EOF)
		{
			code += ch;
			ch = fgetc(str);
		}
		return true;
	}


protected:
	FileInfo _info[256];
};