溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++實現(xiàn)文件壓縮及解壓縮

發(fā)布時間:2020-06-23 06:19:49 來源:網(wǎng)絡(luò) 閱讀:6066 作者:zgw285763054 欄目:編程語言

    原理:Huffman樹的應(yīng)用:Huffman編碼,為出現(xiàn)頻率較高的字符指定較短的碼字,而為出現(xiàn)頻率較低的字符指定較短的碼字,可以實現(xiàn)二進制文件的壓縮。


Heap.h

#pragma once

#include <vector>

//仿函數(shù)
template<class T>
struct Lesser
{
	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 Compare = Lesser<T>>
class Heap
{
public:
	Heap()
	{}

	Heap(const 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)
		{
			_AdjustDown(i);
		}
	}

	void Push(const T& x)
	{
		_a.push_back(x);
		_AdjustUp(_a.size()-1);
	}

	void Pop()
	{
		assert(!_a.empty());

		swap(_a[0], _a[_a.size()-1]);
		_a.pop_back();
		_AdjustDown(0);
	}

	T& Top()
	{
		assert(!_a.empty());

		return _a[0];
	}

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

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

protected:
	void _AdjustUp(int child)
	{
		Compare cmp;
		int parent = (child-1)/2;

		while (child > 0)//parent>=0 ?
		{
			if (cmp(_a[child], _a[parent]))
			{
				swap(_a[child], _a[parent]);
				child = parent;
				parent = (child-1) / 2;
			}
			else
			{
				break;
			}
		}
	}

	void _AdjustDown(int parent)
	{
		Compare cmp;
		int child = parent*2 + 1;

		while (child < _a.size())
		{
			if (child+1 < _a.size() && cmp(_a[child+1], _a[child]))
			{
				++child;
			}

			if (cmp(_a[child], _a[parent]))
			{
				swap(_a[child], _a[parent]);
				parent = child;
				child = parent*2 + 1;
			}
			else
			{
				break;
			}
		}
	}

protected:
	vector<T> _a;
};


HuffmanTree.h

#pragma once

#include "Heap.h"

template<class T>
struct HuffmanTreeNode
{
	HuffmanTreeNode<T>* _left;
	HuffmanTreeNode<T>* _right;
	HuffmanTreeNode<T>* _parent;
	T _weight;

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

template<class T>
class HuffmanTree
{
	typedef HuffmanTreeNode<T> Node;

public:
	HuffmanTree()
		:_root(NULL)
	{}

	~HuffmanTree()
	{
		_Destory(_root);
	}

	HuffmanTree(const T* a, size_t size, const T& invalid)
	{
		_root = _CreateTree(a, size, invalid);
	}

	Node* GetRoot()
	{
		return _root;
	}

protected:
	Node* _CreateTree(const T* a,size_t size, const T& invalid)
	{
		assert(a);

		struct Compare
		{
			bool operator()(const Node* l, const Node* r)
			{
				return l->_weight < r->_weight;
			}
		};

		Heap<Node*, Compare> minHeap;
		for (size_t i = 0; i < size; ++i)
		{
			if (a[i] != invalid)
			{
				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; 
			left->_parent = parent;
			right->_parent = parent;

			minHeap.Push(parent);
		}

		return minHeap.Top();
	}

	void _Destory(Node* root)
	{
		if (root == NULL)
			return;

		_Destory(root->_left);
		_Destory(root->_right);
	}

protected:
	Node* _root;
};

void HuffmanTreeTest()
{
	int a[] = {0,1,2,3,4,5,6,7,8,9};
	HuffmanTree<int> ht(a, 10, -1);

}


FileCompress.h

#pragma once
#include "HuffmanTree.h"
#include <Windows.h>

struct CharInfo
{
	char _ch;
	int _count;
	string _code;

	CharInfo(unsigned char ch = 0)
		:_ch(ch)
		,_count(0)
	{}

	CharInfo operator+(const CharInfo& x)
	{
		CharInfo tmp;
		tmp._count = _count + x._count;
		return tmp;
	}

	bool operator!=(const CharInfo& x) const
	{
		return _count != x._count;
	}

	bool operator<(const CharInfo& x) const
	{
		return _count < x._count;
	}

};

template<class T>
class FileCompress
{
public:
	FileCompress()
	{
		for (size_t i = 0; i < 256; ++i)
		{
			_infos[i] = i;
		}
	}

	void Compress(const char* filename)
	{
		assert(filename);

		FILE* fOutFile = fopen(filename, "rb");
		assert(fOutFile);

		char ch = fgetc(fOutFile);

		int charCount = 0;//統(tǒng)計字符總數(shù)
		
		while (!feof(fOutFile))
		{
			++charCount;
			_infos[(unsigned char)ch]._count++;

			ch = fgetc(fOutFile);
		}

		//創(chuàng)建Huffman樹
		CharInfo invalid(0);
		HuffmanTree<CharInfo> t(_infos, 256, invalid);
		//由Huffman樹生成Huffman編碼
		_GenerateHuffmanCode(t.GetRoot());

		//壓縮
		string compressFilename = filename;
		compressFilename += ".compress";
		FILE* fInCompress = fopen(compressFilename.c_str(), "wb");
		assert(fInCompress);

		fseek(fOutFile, 0, SEEK_SET);
		ch = fgetc(fOutFile);
		char value = 0;
		int size = 0;
		while (!feof(fOutFile))
		{
			string& code = _infos[(unsigned char)ch]._code;

			for (size_t i = 0; i < code.size(); ++i)
			{
				value <<= 1;
				
				if (code[i] == '1')
				{
					value |= 1;
				}

				++size;

				if (size == 8)
				{
					fputc(value, fInCompress);
					size = 0;
					value = 0;
				}
			}

			ch = fgetc(fOutFile);
		}

		if (size > 0)//補位
		{
			value <<= (8-size);
			fputc(value, fInCompress);
		}

		//寫配置文件,方便解壓縮時讀取
		string configFilename = filename;
		configFilename += ".config";
		FILE* fInConfig = fopen(configFilename.c_str(), "wb");
		assert(fInConfig);

		string line;
		char buffer[128];
		
		//將字符總數(shù)寫入配置文件第一行
		line += itoa(charCount, buffer, 10);
		line += "\n";
		fputs(line.c_str(), fInConfig);
		line.clear();

		for (size_t i = 0; i < 256; ++i)
		{
			if (_infos[i]._count)
			{
				line += _infos[i]._ch;
				line += ',';
				line += itoa(_infos[i]._count, buffer, 10);
				line += '\n';

				fputs(line.c_str(), fInConfig);
			}

			line.clear();
		}

		fclose(fOutFile);
		fclose(fInCompress);
		fclose(fInConfig);
	}

	void UnCompress(const char* filename)
	{
		//讀取配置文件
		string configFilename = filename;
		configFilename += ".config";
		FILE* fOutConfig = fopen(configFilename.c_str(), "rb");
		assert(fOutConfig);

		string line;
		
		//讀取字符總數(shù)
		_ReadLine(fOutConfig, line);
		int charCount = atoi(line.c_str());
		line.clear();

		while (_ReadLine(fOutConfig, line))
		{
			if (!line.empty())
			{
				unsigned char ch = line[0];
				_infos[ch]._count = atoi(line.substr(2).c_str());
				
				line.clear();
			}
			else
			{
				line.clear();
				_ReadLine(fOutConfig, line);

				unsigned char ch = '\n';
				_infos[ch]._count = atoi(line.substr(1).c_str());

				line.clear();
			}
		}

		//重建Huffman樹
		CharInfo invalid(0);
		HuffmanTree<CharInfo> t(_infos, 256, invalid);
		
		//讀.compress文件,寫.uncompress文件
		string compressFilename = filename;
		compressFilename += ".compress";
		FILE* fOutCompress = fopen(compressFilename.c_str(), "rb");
		assert(fOutCompress);

		string uncompressFilename = filename;
		uncompressFilename += ".uncompress";
		FILE* fInUncompress = fopen(uncompressFilename.c_str(), "wb");
		assert(fInUncompress);

		HuffmanTreeNode<CharInfo>* root = t.GetRoot();
		HuffmanTreeNode<CharInfo>* cur = root;

		int pos = 7;
		char ch = fgetc(fOutCompress);
		while (1)
		{
			if (ch & (1<<pos))
				cur = cur->_right;
			else
				cur = cur->_left;

			if (cur->_left == NULL && cur->_right == NULL)
			{
				fputc(cur->_weight._ch, fInUncompress);

				cur = root;

				if (--charCount == 0)//字符已讀取完,遇到補位的0不再讀取
				{
					break;
				}
			}

			--pos;
			if (pos == -1)
			{
				ch = fgetc(fOutCompress);
				pos = 7;
			}
		}

		fclose(fOutCompress);
		fclose(fInUncompress);

	}


protected:
	void _GenerateHuffmanCode(HuffmanTreeNode<CharInfo>* root)
	{
		if (root == NULL)
			return;
		
		_GenerateHuffmanCode(root->_left);
		_GenerateHuffmanCode(root->_right);

		if (root->_left == NULL && root->_right == NULL)
		{
			HuffmanTreeNode<CharInfo>* cur = root;
			HuffmanTreeNode<CharInfo>* parent = root->_parent;
			string& code = _infos[(unsigned char)cur->_weight._ch]._code;

			while (parent)
			{
				if (parent->_left == cur)
					code += '0';
				if (parent->_right == cur)
					code += '1';

				cur = parent;
				parent = cur->_parent;
			}

			reverse(code.begin(), code.end());
		}

	}

	bool _ReadLine(FILE* filename, string& line)
	{
		char ch = fgetc(filename);
		
		if (ch == EOF)
			return false;

		while (ch != EOF && ch != '\n')
		{
			line += ch;
			ch = fgetc(filename);
		}

		return true;
	}

protected:
	CharInfo _infos[256];
};

void CompressTest()
{
	//壓縮
	FileCompress<CharInfo> compress;

	int CompressBegin = GetTickCount();

	compress.Compress("Input.BIG");

	int CompressEnd = GetTickCount();

	cout<<"壓縮用時:"<<CompressEnd-CompressBegin<<" ms"<<endl;
}

void UncompressTest()
{
	//解壓縮
	FileCompress<CharInfo> uncompress;

	int UncompressBegin = GetTickCount();

	uncompress.UnCompress("Input.BIG");

	int UncompressEnd = GetTickCount();

	cout<<"解壓縮用時:"<<UncompressEnd-UncompressBegin<<" ms"<<endl;
}

Test.cpp

#include <iostream>
using namespace std;
#include <assert.h>

#include "FileCompress.h"

int main()
{
	CompressTest();

	UncompressTest();
	return 0;
}



下面是對一個大小為8.04MB文件的測試:


C++實現(xiàn)文件壓縮及解壓縮


結(jié)果成功壓縮、解壓縮:


C++實現(xiàn)文件壓縮及解壓縮


壓縮后的文件大?。?/span>


C++實現(xiàn)文件壓縮及解壓縮


壓縮后的文件:


C++實現(xiàn)文件壓縮及解壓縮


配置文件:


C++實現(xiàn)文件壓縮及解壓縮


用Beyond Compare文本比較工具檢查原文件與解壓后的文件:


C++實現(xiàn)文件壓縮及解壓縮


無差異


C++實現(xiàn)文件壓縮及解壓縮




向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI