大家好!过完新年后,在宿舍闲着蛋疼,就把上学期无法实现的哈夫曼树的压缩及其解压实现一下了。至于怎么压缩各种格式的文件,现在还没有找到实现方法。
以下是代码实现:
//Huffman_H.h #ifndef Huffman_H #define Huffman_H #include <string> #include <fstream> #include <queue> #include <vector> using namespace std; const int MAX_SIZE = 256; //含有256个ACSII码 struct huffman_node { char id; int freq; string code; huffman_node* left; huffman_node* right; huffman_node* parent; //默认的构造函数 huffman_node(char ch = '\0',string s = "", int cou = 0,huffman_node* lef = NULL, huffman_node* rig = NULL,huffman_node* par = NULL): id(ch),code(s),freq(cou),left(lef),right(rig),parent(par){} }; typedef huffman_node* node_ptr; class Huffman { protected: node_ptr node_array[MAX_SIZE]; fstream in_file; fstream out_file; ofstream decompress_file; //解压使用的文件输入输出流 node_ptr child; node_ptr parent; char id; int decodingnum; //记录解码时有多少种不同的字符 string in_file_name; string out_file_name; class compare { public: bool operator()(const node_ptr& c1,const node_ptr& c2) const { return (*c1).freq > (*c2).freq; } }; //用于比较优先队列中元素间的顺序 priority_queue<node_ptr,vector<node_ptr>,compare > pq; //根据输入文件构造包含字符及其频率的数组 void create_node_array(); public: node_ptr root; //根据输入和输出流初始化对象 Huffman(string s1,string s2); //构造优先队列 void create_pq(); //构造Huffman树 void create_huffman_tree(); //计算Huffman编码 void calculate_huffman_codes(node_ptr & root); //将Huffman编码和编码后的文本存入文件 void save_to_file(); //将压缩好的文件解压回去 void decompress_to_file(); }; #endif
//Huffman.cpp #include "Huffman_H.h" #include <iostream> #include <fstream> void Huffman::create_node_array() { unsigned char ch; //定义读取的字节单位 int num = 0; in_file.open(in_file_name,ios::in | ios::binary); //使用二进制读入文件即可对所有的文件格式进行操作 in_file.read((char *)&ch,sizeof(char)); //一个字节一个字节的读入,并存储相应的字符极其权重 while(!in_file.eof()) { num = int(ch); if(node_array[num]->freq == 0) { node_array[num]->id = ch; } node_array[num]->freq++; in_file.read( (char *)&ch, sizeof(char)); } in_file.close(); //将字符极其权重存入优先队列中进行排序 } //构造函数,主要负责初始化工作 Huffman::Huffman(string s1,string s2) { in_file_name = s1; out_file_name = s2; root = NULL; parent = NULL; child = NULL; decodingnum = 0; int i; //这里值得注意,因为要给256个哈夫曼树的结点划分空间 for(i = 0;i < MAX_SIZE;i++) { node_array[i] = new huffman_node(); } } //构造优先队列 void Huffman::create_pq() { create_node_array(); int i; for(i = 0; i < MAX_SIZE;i++) { if(node_array[i]->freq > 0) { decodingnum++; pq.push(node_array[i]); } } } //构造Huffman树 void Huffman::create_huffman_tree() { while(pq.size() > 1) ////在队列中的数在一个以上时进行节点合并构建Huffman树 { node_ptr h1,h2; h1 = pq.top(); pq.pop(); h2 = pq.top(); pq.pop(); node_ptr h = new huffman_node(); h->freq = h1->freq + h2->freq; h->left = h1; h->right = h2; h1->parent = h; h2->parent = h; pq.push(h); //按照huffman算法,再把它放回优先队列里面 } root = pq.top(); root->parent = NULL; } //计算Huffman编码 void Huffman::calculate_huffman_codes(node_ptr & root) { //若哈夫曼树为空,则不操作. if(root == NULL) ; else { if(root->left != NULL) { root->left->code = root->code + '0'; calculate_huffman_codes(root->left); } if(root->right != NULL) { root->right->code = root->code + '1'; calculate_huffman_codes(root->right); } } } //将Huffman编码和编码后的文本存入文件 void Huffman::save_to_file() { int bytenum = 0; //记录字节的位数,每8位刷新一次 unsigned char temp,ch = 0; int i; out_file.open(out_file_name,ios::out|ios::binary); //将编码的情况写入压缩文件里 out_file.write((char*)&decodingnum,sizeof(char)); //将编码情况先存入压缩文件中以便在解压时使用,有多少种不同类型的字符 for(i = 0;i < MAX_SIZE;i++) { if(node_array[i]->freq != 0) { //将字符和频率逐个字符读入 out_file.write((char *)&node_array[i]->id,sizeof(char)); out_file.write((char *)&node_array[i]->freq,sizeof(char)); } } in_file.open(in_file_name,ios::in|ios::binary); in_file.read((char*)&temp,sizeof(char)); while(!in_file.eof()) { for(i = 0;i < node_array[temp]->code.size();i++) { ch <<= 1; //将读到的字符左移一位 bytenum++; if(node_array[temp]->code[i] == '1') ch = ch | 1; if ( bytenum == 8 ) //当字节位移满八次以后进行一次压缩 { out_file.write( (char *)&ch, sizeof(char) ); bytenum = 0; ch = 0; } } in_file.read((char*)&temp,sizeof(char)); } //如果压缩的字节不足8位,则用0补足 if(bytenum > 0 && bytenum < 8) { ch <<= (8 - bytenum); out_file.write((char*)&ch,sizeof(char)); } in_file.close(); out_file.close(); } //将压缩好的文件解压回去 void Huffman::decompress_to_file() { int temp,num,i,totalcount; unsigned char ch; decompress_file.open(out_file_name); out_file.open(in_file_name,ios::in|ios::binary); out_file.read((char*)&ch,sizeof(char)); decodingnum = int(ch); //表头所记录的不同类型的字符数目 num = decodingnum; cout << num << endl; cout << out_file_name <<endl; out_file.read((char*)&ch,sizeof(char)); while(num) ////从新的压缩文件中读取编码情况 { temp = (int)ch; node_array[temp]->id = ch; out_file.read((char*)&ch,sizeof(char)); node_array[temp]->freq = int(ch); pq.push(node_array[temp]); out_file.read((char*)&ch,sizeof(char)); num--; } create_huffman_tree(); totalcount = root->freq; while(!out_file.eof()) { for(i = 7;i >= 0;i--) { temp = (ch >> i)&1; //每次提出最开头的一位,这样表达的好处是ch并没改变其值 if(root->left == NULL && root->right == NULL) //当到达哈夫曼树的叶子时 { decompress_file << root->id; root = pq.top(); //返回哈夫曼树顶部 decodingnum--; if(!totalcount) return; } //当为"0",向左子树迭代 if(temp == 0) { root = root->left; } //当为"1",向右子树迭代 else { root = root->right; } } out_file.read((char*)&ch,sizeof(char)); } out_file.close(); decompress_file.close(); }
//test.cpp #include <iostream> #include <string> #include <fstream> #include "Huffman_H.h" using namespace std; /* int main(int argc,char *argv[]) { if(argc != 3) { cout << "Usage:\n\t huffmancoding inputfile outputfile" << endl; exit(1); } Huffman h(argv[1],argv[2]); h.create_pq(); h.create_huffman_tree(); h.calculate_huffman_codes(h.root); h.save_to_file(); cout << endl; return 0; } */ /*压缩情况 int main() { string s1,s2; cin >> s1; cin >> s2; Huffman h(s1,s2); h.create_pq(); h.create_huffman_tree(); h.calculate_huffman_codes(h.root); h.save_to_file(); cout << "Done" << endl; return 0; } */ // int main() { string s1,s2; cin >> s1; cin >> s2; Huffman h(s1,s2); h.decompress_to_file(); cout << "Done!" << endl; return 0; } //