//HuffmanTree.h
#include <iostream>
#include <string.h>
#include "Heap.h"
using namespace std;
template<class T>
struct HuffmanNode
{
HuffmanNode<T>* _parent;
HuffmanNode<T>* _left;
HuffmanNode<T>* _right;
T _weight;
HuffmanNode(const T& x)
:_parent(NULL)
, _left(NULL)
, _right(NULL)
, _weight(x)
{
}
};
template <class T>
class Huffman
{
typedef HuffmanNode<T> Node;
public:
Huffman()
:_root(NULL)
{}
Huffman(T* a,size_t size,const T& invalid)
{
assert(a);
struct Less
{
bool operator()(const Node* left, const Node* right)
{
return left->_weight < right->_weight;
}
};
Heap<Node*, Less> MinHeap;
for (size_t i = 0; i < size; i++)
{
if (a[i] != invalid)
{
MinHeap.Push(new Node(a[i]));
}
}
while (MinHeap.Size()>1)
{
Node* L = MinHeap.Top();
MinHeap.Pop();
Node* R = MinHeap.Top();
MinHeap.Pop();
Node* root = new Node(L->_weight + R->_weight);//构建父节点
root->_left = L;
root->_right = R;
L->_parent = root;
R->_parent = root;
MinHeap.Push(root);//放入堆中重新调整
}
_root = MinHeap.Top();
}
Node* GetNode()
{
return _root;
}
private:
Node* _root;
};
//HuffmanTreeCompress.h
#pragma once
#include "HuffmanTree.h"
#include <string>
#include <assert.h>
#include<algorithm>
typedef long long LongType;
struct CharInfo
{
unsigned char _ch; //这样定义压缩汉子是就不会出现乱码问题了
LongType _CharCount; //字符出现次数
string _code; //huffman编码
CharInfo(LongType CharCount = 0)
:_ch(0)
, _CharCount(CharCount)
{
}
CharInfo operator+(const CharInfo& c)
{
CharInfo tmp;
tmp._CharCount = _CharCount + c._CharCount;
return tmp;
}
bool operator != (const CharInfo c)
{
return _CharCount != c._CharCount;
}
bool operator < (const CharInfo& c)const
{
return _CharCount < c._CharCount;
}
};
class FileCompress
{
public:
FileCompress()
{
for (int i = 0; i < 256;i++)// 读取字符对应ASCII表,将字符存在数组下标与字符ASCLL码相等的位置
{
_str[i]._ch = i;
}
}
void Compress(const char* filename)
{
assert(filename);
FILE* fout = fopen(filename,"rb");// 使用而进制形式读,否则读不到汉字
assert(fout);
int ch = fgetc(fout);
while (!feof(fout))//未读到文件结尾
{
_str[ch]._CharCount++; //统计次数
ch = fgetc(fout); //继续向后读字符
}
CharInfo invalid(0); //定义一个非法值,当CharCount=0时就相当于一个非法值,无需根据权值构建Huffman树
Huffman<CharInfo> hf(_str,256,invalid); //根据字符次数构建huffman树
string code;
_GetHuffmanCode(hf.GetNode(), code);
string CompressFilename = filename; //将编码写入压缩文件中
CompressFilename += ".compress";
FILE* Input = fopen(CompressFilename.c_str(),"wb");
assert(Input);
fseek(fout,0,SEEK_SET);
char Inputch = 0; //写入文件压缩编码
int size = 0;
ch = fgetc(fout);
while (!feof(fout))
{
string& code = _str[ch]._code;
for (size_t i = 0; i < code.size();++i)
{
Inputch <<=1;
if (code[i]=='1')
{
Inputch |= 1;
}
++size;
if (size == 8)
{
fputc(Inputch, Input);
size = 0;
Inputch = 0;
}
}
ch = fgetc(fout);
}
if (size>0)
{
Inputch <<= 8 - size;
fputc(Inputch,Input);
}
fclose(fout);
fclose(Input);
//配置文件
string ConfigFilename = filename;
ConfigFilename += ".config";
FILE* FInConfig = fopen(ConfigFilename.c_str(),"wb");
string line;
for (size_t i = 0; i < 256; ++i)
{
if (_str[i]._CharCount!=0)
{
line +=_str[i]._ch;
line += ',';
char buff[20] = { 0 };
line += _itoa(_str[i]._CharCount, buff, 10);//转换为10进制
line += '\n';
fwrite(line.c_str(),1,line.size(),FInConfig);
line.clear();
}
}
fclose(FInConfig);
}
void Uncompress(const char* filename)
{
string ConfigFile = filename;
ConfigFile += ".config";
FILE* foutconfig = fopen(ConfigFile.c_str(), "rb");
assert(foutconfig);
string line;
while (ReadLine(foutconfig, line))
{
if (line.empty())
{
line += '\n';
continue;
}
else
{
unsigned char ch = line[0];
_str[ch]._CharCount = atoi(line.substr(2).c_str());
line.clear();
}
}
CharInfo invalid(0);
Huffman<CharInfo> hf(_str, 256, invalid);
string uncompressfilename = filename;
uncompressfilename += ".uncompress";
FILE* fin = fopen(uncompressfilename.c_str(), "wb");
assert(fin);
string compressfilename = filename;
compressfilename += ".compress";
FILE* fout = fopen(compressfilename.c_str(), "rb");
assert(fout);
HuffmanNode<CharInfo> *root = hf.GetNode();
LongType ChLen = root->_weight._CharCount;
HuffmanNode<CharInfo> *cur = root;
int pos = 7;
int ch = fgetc(fout);
while (ChLen)
{
if (ch&(1<<pos))
{
cur = cur->_right;
}
else
cur = cur->_left;
--pos;
if (cur->_left==NULL&&cur->_right==NULL)
{
fputc(cur->_weight._ch, fin);
cur = root;
--ChLen;
if (ChLen==0)
break;
}
if (pos==-1)
{
ch = fgetc(fout);
pos = 7;
}
}
fclose(foutconfig);
fclose(fin);
fclose(fout);
}
protected:
void _GetHuffmanCode(HuffmanNode<CharInfo>* root,string& code)
{
if (root==NULL)
{
return;
}
if (root->_left==NULL&&root->_right==NULL)
{
_str[root->_weight._ch]._code = code;
}
_GetHuffmanCode(root->_left, code + '0');
_GetHuffmanCode(root->_right, code + '1');
}
bool ReadLine(FILE *fout, string& line)
{
int ch = fgetc(fout);
if (feof(fout))
{
return false;
}
while (!feof(fout)&&ch!='\n')
{
line += ch;
ch = fgetc(fout);
}
return true;
}
protected:
CharInfo _str[256];
};
//Heap.h
#pragma once
#include<iostream>
#include <vector>
#include <assert.h>
using namespace std;
template<class T>
struct Less
{
bool operator()(const T& left, const T& right)
{
return left < right;
}
};
template<class T>
struct Larger
{
bool operator()(const T& left, const T& right)
{
return left > right;
}
};
template<class T,class Compare=Larger<T>>
class Heap
{
public:
Heap()
{}
Heap(T* a,size_t size)
{
assert(a);
_a.reserve(size);
for (size_t i = 0; i < size;i++)
{
_a.push_back(a[i]);
}
for (size_t i = (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);
}
const T& Top()
{
assert(!_a.empty());
return _a[0];
}
bool Empty()
{
return _a.empty();
}
size_t Size()
{
return _a.size();
}
protected:
void AdjustDown(size_t root)
{
Compare com;
size_t parent = root;
size_t child = root * 2 + 1; //左孩子
while (child<_a.size())
{
if (child + 1 < _a.size() && com(_a[child+1], _a[child])) //右孩子存在,比较左右孩子
{
++child;
}
if (com(_a[child], _a[parent]))//右孩子不存在,比较左孩子和父节点
{
std::swap(_a[parent], _a[child]);
parent = child; //向下调整
child = parent * 2 + 1;
}
else
break;
}
}
void AdjustUp(size_t child)
{
while (child>0) //不是根节点
{
Compare com;
size_t parent = (child - 1) / 2;
if (com(_a[child],_a[parent]))
{
std::swap(_a[parent],_a[child]);
child = parent; //向上调整
}
else
break;
}
}
private:
vector<T> _a;
};
//FileCompress.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "HuffmanTreecompress.h"
#include<windows.h>
void test()
{
FileCompress f;
cout << "开始压缩" << endl;
cout << "压缩用时:";
int start = GetTickCount();
// f.Compress("test.txt");
// f.Compress("1.mp3");
f.Compress("2.jpg");
int end = GetTickCount();
cout << end - start << endl;
cout << "开始解压" << endl;
cout << "解缩用时:";
start = GetTickCount();
//f.Uncompress("test.txt");
//f.Uncompress("1.mp3");
f.Uncompress("2.jpg");
end = GetTickCount();
cout << end - start << endl;
}
int main()
{
test();
system("pause");
return 0;
}