Huffman编码实现文件的压缩与解压缩。

时间:2023-01-27 10:06:14

以前没事的时候写的,c++写的,原理很简单,代码如下:

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <iostream>
  4 #include <string>
  5 //#include <bitset>
  6 #include <fstream>
  7 #include <ctime>
  8 
  9 const int maxCodeNum = 256;
 10 
 11 using namespace std;
 12 
 13 //哈夫曼树的树节点
 14 struct HaffTreeNode{
 15     HaffTreeNode * lNode;
 16     HaffTreeNode * rNode;
 17     string  haffCode;
 18     int value;
 19     int alpha;
 20     HaffTreeNode()
 21         :lNode(NULL), rNode(NULL), haffCode(""), value(0), alpha(0){;}
 22 };
 23 
 24 //链表节点,用于生成哈夫曼树
 25 struct ListNode{
 26     struct HaffTreeNode HaffTreeNode;
 27     ListNode *nextListNode;
 28     ListNode()
 29         :nextListNode(NULL){;}
 30 };
 31 
 32 //用与保存输入文件统计信息的hash表
 33 typedef struct HashTable{
 34     int value;
 35     int alpha;
 36     HashTable()
 37         :value(0), alpha(0){}
 38     //比较函数用于排序使用
 39     inline friend int operator-(const HashTable & a, const HashTable & b){
 40         return a.value - b.value;
 41     }
 42 } HashTable;
 43 HashTable charHashTable[maxCodeNum];
 44 
 45 
 46 //排序使用的比较大小的函数
 47 int hashComp(const void * a, const void * b)
 48 {
 49     return  *((HashTable *)a) - *((HashTable *)b);
 50 }
 51 
 52 
 53 //创建一个哈夫曼树
 54 HaffTreeNode * createHaffTreeNodeTree(HashTable table[])
 55 {
 56     ListNode *root = new ListNode;
 57     ListNode *next = root;
 58     for(int i = 0; /*i < maxCodeNum - 1*/; ++i){
 59         if(table[i].value == 0)//如果对应的码不为0,就为其分配一个树节点
 60             continue;
 61         next->HaffTreeNode.alpha = table[i].alpha;
 62         next->HaffTreeNode.value = table[i].value;
 63         if(i ==maxCodeNum - 1)
 64             break;
 65         next->nextListNode = new ListNode;
 66         next = next->nextListNode;
 67     }
 68 
 69     while(root->nextListNode != NULL){
 70         ListNode * currNode = new ListNode;
 71         currNode->HaffTreeNode.value = root->HaffTreeNode.value + root->nextListNode->HaffTreeNode.value;
 72         currNode->HaffTreeNode.lNode =  &(root->HaffTreeNode);
 73         currNode->HaffTreeNode.rNode =  &(root->nextListNode->HaffTreeNode);
 74         root = root->nextListNode->nextListNode;    //概率最小的两个码相加组成一个新的节点
 75 
 76         ListNode * nextNode = root;
 77         ListNode * prevNode = NULL;
 78         while(nextNode != NULL && currNode->HaffTreeNode.value > nextNode->HaffTreeNode.value){
 79             prevNode = nextNode;
 80             nextNode = nextNode->nextListNode;
 81         }
 82 
 83         if(prevNode == NULL){//将这个新的节点插入到所有节点之前(currNode目前还是最小的)
 84             currNode->nextListNode = nextNode;
 85             root = currNode;
 86         }else{//插入到节点中间或者节点之后的位置
 87             prevNode->nextListNode = currNode;
 88             currNode->nextListNode = nextNode;
 89         }
 90     }//在这个list中所有的元素遍历完成之后返回
 91     return &(root->HaffTreeNode);//返回书的根节点的哈弗满节点,这个节点已经构造成为了一棵树
 92 }
 93 
 94 string huffmanCodeTable[maxCodeNum];
 95 string haffCode;
 96 
 97 //给哈夫曼树编码
 98 void createHaffmanTable(HaffTreeNode * root)
 99 {
100     if(root->lNode == NULL && root->rNode == NULL){
101         huffmanCodeTable[root->alpha] = haffCode;
102         haffCode.erase(haffCode.length() - 1);
103         return;
104     }//给各个节点赋予相应的哈夫曼编码
105     haffCode.append("0");
106     createHaffmanTable(root->lNode);
107 
108     haffCode.append("1");
109     createHaffmanTable(root->rNode);
110 
111     if(!haffCode.empty()){
112         haffCode.erase(haffCode.length() - 1);
113     }
114     return;
115 }
116 
117 //将生成的二进制长串编码转换成字符用于存储在压缩文件中
118 unsigned char StrToBin(string str)
119 {
120     unsigned int ans =0;
121     int tmpNum = atoi(str.c_str());
122     int multiNum = 1;
123     while(tmpNum != 0){
124         ans += tmpNum%10*multiNum;
125         tmpNum/=10;
126         multiNum *= 2;
127     }
128     return (unsigned char) ans;
129 }
130 
131 //用于将压缩文件的字符转换成huffman编码
132 string BinToStr(unsigned char c)
133 {
134     string tmpNumStr;
135     while(c != 0){
136         tmpNumStr.insert(tmpNumStr.begin(), (unsigned char)(c%2 + '0'));
137         c /= 2;
138     }
139     if(tmpNumStr.length() < 8){
140         tmpNumStr.insert(tmpNumStr.begin(),  8 - tmpNumStr.length(), '0');
141     }
142     return tmpNumStr;
143 }
144 
145 //下面是将huffman码译成原字符的程序
146 char huffDecode(HaffTreeNode * root, string & code)
147 {
148     unsigned int i;
149     for( i = 0; i < code.length(); ++i){
150         if(root->alpha == 0)
151             root = (code[i] - '0')?root->rNode:root->lNode;
152         else{
153             code.erase(0, i);
154             return root->alpha;
155         }
156     }
157     if(root->alpha !=0){
158         code.erase(0, i);
159         return root->alpha;
160     }
161     code.clear();
162     return '\0';
163 }
164 
165 
166 
167 int main(int argc, char ** argv)
168 {
169     if(argc != 3){
170         printf("Error number of arguments!\n");
171     }
172     FILE * fin = fopen(argv[1], "r");
173     int c = 0;
174     while((c = fgetc(fin)) != EOF && c != '\n'){
175         putchar(c);
176         putchar('*');
177         charHashTable[c].alpha = c;
178         charHashTable[c].value++;
179     }
180 
181     qsort(charHashTable, sizeof(charHashTable)/sizeof(charHashTable[0]),
182             sizeof(charHashTable[0]), hashComp);
183     /*建立有关本文件的huffman树*/
184     HaffTreeNode * haffTreeRoot = createHaffTreeNodeTree(charHashTable);
185     createHaffmanTable(haffTreeRoot);
186 
187     cout << "Char\tTimes\tCodes";
188     for(int i = 0; i < maxCodeNum; ++i){
189         if(charHashTable[i].value != 0){
190             cout << (char)charHashTable[i].alpha << "\t" << charHashTable[i].value
191                  << "\t" << huffmanCodeTable[charHashTable[i].alpha] << "\n";
192         }
193     }
194 
195     FILE * fout;
196     if((fout = fopen(argv[2], "w")) == NULL){
197         perror("open output file error!\n");
198     }
199     rewind(fin);
200     string buf; 
201 
202     while((c = fgetc(fin)) != EOF){ /*将文件通过huffman码转来进行压缩*/
203         //printf("The char is %c  ", c);
204         buf += huffmanCodeTable[c];
205         cout << buf << endl;
206         if(buf.length() > 8){   //当转换的字符得到的huffman码达到8的时候转换成一个字符填入目标文件
207             fputc(StrToBin(buf.substr(0, 8)), fout);
208             buf.erase(0, 8);
209         }
210     }
211 
212     int leftZero = 0;   //保存不到8位的余留位的个数
213     if(!buf.empty()){
214         buf.append((leftZero = 8 - buf.length()), '0');
215         fputc(StrToBin(buf), fout);
216     }
217 
218     if(fclose(fin) == -1)
219         perror("close file error!\n");
220     if(fclose(fout) == -1)
221         perror("close file error!\n");
222 
223     if((fin = fopen(argv[2], "rb")) == NULL)//打开压缩文件,开始解码
224         perror("Open file error!\n");
225     if((fout = fopen("huffmanDecompose.txt", "w")) == NULL)
226         perror("Open file error!\n");
227 
228     //开始解码
229     int bin;
230     buf.clear();
231     while((bin = fgetc(fin)) != EOF){
232         buf.append(BinToStr(bin));
233     }
234 
235     while(buf.length() - leftZero != 0 && !buf.empty()){
236         fputc(huffDecode(haffTreeRoot, buf), fout);
237     }
238     if(fclose(fin) != 0)
239         perror("close file error!\n");
240     if(fclose(fout) != 0)
241         perror("close file error!\n");
242     return 0;
243 }

./a.out file1 file2
file1:输入文件
file2:输出文件(压缩后)
要锁完成后会将文压缩文件解压到huffmanDecompose.txt这个文件中