之前看到cppblog一篇关于huffman的文,和我今早的一个梦不谋而合。我记得似乎曾经给前女友写过一个Huffman的课程大作业,花了当天晚上的一些时间,只是为了完成任务而写的,草草的回忆了一下huffman的原理,然后就开始写了,当时因为她的作业并没要求规模,我只把控制台输入端作为文件输入,先压缩再解压,并且把所有中间过程输出。

我知道自己有个弱点,当自己不把模块划分得很细,那么DEBUG的时间就会变得相对长一些,但是那一次写哈夫曼编码就完全是认为这种作业就不必划分得太细也能很容易实现。结果今早由于梦见一个诡异的梦(梦见自己得了绝症,她那弱不禁风的身影在梦里不断浮现...),又看到有人写了篇哈夫曼编码的随笔,我就重新打开那个我写的工程运行了一次。

嗯,运行正确,然后我找来一篇英文小说复制了一大片进去,结果bug出现了。接着是我不断找bug的过程(比如当时规模设置得太小,只是为了给她应付作业;再比如bit位批量处理类型等等细节...)

在搞定两个bug,最终还有一个诡异的bug没解决,I give up. 我知道这个错误之所以难以找到,是因为自己为了随意发挥违背自己历来遵从的编程原则,就算找到那个bug也无济于事,不如以后不再任性,把模块按down-top、top-down划分(这种方法几乎能让我不出任何错),并且写代码的时候,为每一次项目的测试单元和调试代码做准备。


当时自己任性写的代码如下(注释是很全面,因为当时要给她去交作业,但是却发现那些注释只是给她老师看的,而非给我自己看的....因为我没有在注释里面详细定义哈夫曼编码压缩后的压缩文件格式信息):

(以下代码的input不能直接处理Unicode,写成重定向到文件可以)

嗯嗯,我还把自己这段代码复制到百度知道回答别人的问题过~~真是贻害四方瓦。

  1 #include  < iostream >
  2
  3 using   namespace  std;
  4
  5 #define  MAX_FILE 50000 // 假设的文件最大长度
  6 #define  MAXLIST 256 // 最大MAP值
  7 #define  MAX_HUFFMAN_LENGTH 100 // 哈夫曼编码长度
  8 unsigned  char  dictionary[MAXLIST][ 2 ] = {0} ; // Hash映射,[][0]为权值,[][1]为字符
  9 unsigned  char  fileContent[MAX_FILE]; // 处理的字符串大小
 10 int  Huffman[MAXLIST][MAX_HUFFMAN_LENGTH] = {2} ; // 哈夫曼编码序列
 11 unsigned  char  HuffmanList[MAXLIST] = {0} ; // 哈夫曼编码对应的字符有序序列
 12 unsigned  char  HuffFileCode[MAX_FILE] = {0} ; // 哈夫曼编码字符串序列
 13 unsigned  char  HuffFile[MAX_FILE] = {0} ;
 14 // 编码到假设的文件的哈夫曼压缩格式: 依次存储 原字符串长度(1字节存储:可扩展到2字节)、哈夫曼编码数(1字节)、每个哈夫曼编码的长度序列、每个哈夫曼编码对应的字符序列、编码过的哈夫曼字符串
 15
 16 unsigned  char  GetFile[MAX_FILE] = {0} ; // 解码序列
 17
 18 void  ShellSort(unsigned  char  pData[MAXLIST][ 2 ], int  Count) // Shell排序,用于准备有序化要构造的编码权值构造哈夫曼树做准备
 19 {
 20    int step[4]={9,5,3,1};//增量序列
 21
 22    int iTemp,cTemp;
 23    int k,s,w;
 24    for(int i=0;i<4;i++)
 25    {
 26        k=step[i];
 27        s=-k;
 28        for(int j=k;j<Count;j++)
 29        {iTemp=pData[j][0];
 30        cTemp=pData[j][1];
 31        w=j-k;
 32        if(s==0)
 33        {
 34            s=-k;
 35            s++;
 36            pData[s][0]=iTemp;
 37            pData[s][1]=cTemp;
 38        }

 39        while((iTemp<pData[w][0])&&(w>=0)&&(w<=Count))
 40        {
 41            pData[w+k][0]=pData[w][0];//权值交换
 42            pData[w+k][1]=pData[w][1];//字符交换
 43            w=w-k;
 44        }

 45        pData[w+k][0]=iTemp;
 46        pData[w+k][1]=cTemp;
 47        }

 48    }

 49}

 50
 51
 52 struct  TNode // 哈夫曼树结点
 53 {
 54    TNode* pNode;
 55    TNode* lNode;
 56    TNode* rNode;
 57    unsigned char dictionary;
 58    unsigned char weight; 
 59    TNode(unsigned char dic,unsigned char wei)
 60    {
 61        pNode=0;
 62        lNode=0;
 63        rNode=0;
 64        dictionary=dic;
 65        weight=wei;
 66    }

 67}
;
 68
 69 struct  LNode // 链表结点,用于存储哈夫曼树结点,进而构造哈夫曼树(保证每一步链表结点包含的哈夫曼结点都是有序的)
 70 {
 71    LNode* prev;
 72    LNode* next;
 73    TNode* tnode;
 74    LNode()
 75    {
 76        prev=next=0;
 77        tnode=0;
 78    }

 79}
;
 80
 81 int  len = 0 ; // 哈夫曼编码数
 82 int  deep =- 1 ; // 深度
 83 void  Preorder(TNode  *  p); // 前序遍历
 84 void  byLeft(TNode * p) // 经由左结点
 85 {
 86    deep++;
 87    Huffman[len][deep]=0;
 88    Preorder(p);
 89
 90    Huffman[len][deep]=2;
 91    deep--;
 92}

 93 void  byRight(TNode * p) // 经由右结点
 94 {
 95
 96    deep++;
 97    Huffman[len][deep]=1;
 98    Preorder(p);
 99
100    Huffman[len][deep]=2;
101    deep--;
102}

103 void  Preorder(TNode  *  p)
104 {
105
106    if(p->lNode!=0)//当左子结点非空则遍历
107    {
108
109        byLeft(p->lNode);
110    }

111    if(p->rNode!=0)//当右子结点非空则遍历
112    {
113        byRight(p->rNode);
114    }

115
116
117
118    if((p->lNode==0)&&(p->rNode==0))//当左右结点都为空,则增加哈夫曼编码数到另一个记录
119    {
120
121        Huffman[len][deep+1]=2;
122        int i=0;
123        for(;Huffman[len][i]!=2;i++)
124        {
125            Huffman[len+1][i]=Huffman[len][i];
126        }

127        Huffman[len+1][i]=2;
128
129        HuffmanList[len]=p->dictionary;
130
131
132
133        len++;
134    }

135
136}

137
138
139 unsigned  char  generateOne( int  k) // 产生k个连续1的二进制串,比如111,1111,111111,用于编码进假设的文件
140 {
141    unsigned char c=0;
142    for(;k!=0;k--)
143    {
144        c|=(1<<(k-1));
145
146    }

147    return c;
148}

149
150 int  compareBits(unsigned  char  b1,unsigned  char  b2,unsigned  char  c, int  l, int  d) // 判断由 [b1,b2]  组成的16位二进制数以d为起点,是否是长度为l的c二进制串(哈夫曼编码)的前缀
151 {
152    unsigned __int8 t=(((((0x00ff&b1)<<8)|(0x00ff&b2))>>(8-d))&0x00ff);
153    return (((t)&((generateOne(l)<<(8-l))&0xff))==((c<<(8-l))&0xff));
154}

155 void  input(unsigned  char *  _list,unsigned  char  end)
156 {
157
158    int i=0;
159    for(;(_list[i]=cin.get())!=end;i++);
160    _list[i]='\0';
161
162}

163
164 int  main()
165 {
166
167    /**//*  或许假定的文件字符串向量中的字符串 */
168    cout<<"请输入要压缩的字符串:";
169    input(fileContent,'$');
170
171    unsigned int fileLen=0;
172
173
174    /**//*  Hash进dictionary */
175    for(int i=0;fileContent[i]!='\0';i++,fileLen++)
176    {
177
178        ++dictionary[fileContent[i]][0];
179        dictionary[fileContent[i]][1]=fileContent[i];
180    }

181    unsigned int len=0;
182
183    /**//*  把Hash了的dictionary向前靠拢 */
184
185
186    {
187        for(int i=0;i!=MAXLIST;i++)
188        {
189
190            if(dictionary[i][0]!=0)
191            {
192                dictionary[len][0]=dictionary[i][0];
193                dictionary[len][1]=dictionary[i][1];
194                len++;
195            }

196        }

197    }

198    cout<<"哈夫曼编码个数:"<<len<<endl;
199    /**//*  对dictionary按权值进行排序 */
200
201    ShellSort(dictionary,len);
202
203    /**//* 构造链表,链表中放有序dictionary权值的树结点 */
204    LNode* head=new LNode,*p=head;
205    head->next=new LNode;
206    TNode *tempTNode=new TNode(dictionary[0][1],dictionary[0][0]);
207    head->tnode=tempTNode;
208
209
210    {
211        for(int i=0;i!=len-1;i++)
212        {
213            p->next->prev=p->next;
214            p=p->next;
215
216            p->next=new LNode;
217            tempTNode=new TNode(dictionary[i+1][1],dictionary[i+1][0]);
218            p->tnode=tempTNode;
219        }

220    }

221    delete p->next;
222    p->next=0;
223
224    /**//* 每次最小权值的前面两个链表结点中的树结点组成一个子树,子树有合权值,子数的根按权值排序进链表*/
225
226    for(p=head;p->next!=0;)
227    {
228
229        p->tnode->pNode=new TNode('\0',(p->tnode->weight)+(p->next->tnode->weight));
230        p->next->tnode->pNode=p->tnode->pNode;
231        p->tnode->pNode->lNode=p->tnode;
232        p->tnode->pNode->rNode=p->next->tnode;
233        head=p->next;
234        delete p;
235        p=head;
236        p->tnode=p->tnode->pNode;
237        for(LNode* t=head;t->next!=0;t=t->next)
238        {
239            if(t->tnode->weight>t->next->tnode->weight)
240            {
241                TNode* k=t->tnode;
242                t->tnode=t->next->tnode;
243                t->next->tnode=k;
244            }

245        }

246
247    }

248
249
250    int code[MAX_FILE],h=0;
251
252
253    
254    /**//* 前序遍历构造哈夫曼编码 */
255    Preorder(p->tnode);
256
257    {
258        for(int i=0;i!=len;i++)
259            dictionary[HuffmanList[i]][0]=i;
260    }

261
262    int codeLen=0,total=0;
263    {
264
265        for(int i=0;i!=fileLen;i++)
266        {
267            
268            unsigned int j=dictionary[fileContent[i]][0];
269
270            
271            for(int k=0;Huffman[j][k]!=2;k++)
272            {
273
274                HuffFileCode[codeLen]|=(Huffman[j][k]<<(7-total%8));
275
276                code[h++]=Huffman[j][k];
277
278                if(((total+1)%8)==0)
279                {
280                    
281                    HuffFile[4+len*3+2+codeLen]=HuffFileCode[codeLen];
282                    codeLen++;
283                }

284                total++;
285            }

286
287
288
289        }

290    }

291
292    HuffFile[4+len*3+2+codeLen]=HuffFileCode[codeLen];
293
294
295    HuffFile[0]=((fileLen>>24));
296    HuffFile[1]=((fileLen>>16));
297    HuffFile[2]=((fileLen>>8));
298    HuffFile[3]=((fileLen));
299
300
301
302    /**//* 解压缩假定的文件HuffFile成为原字符串序列 */
303    cout<<"哈夫曼编码序列:\n";
304
305    /**//* 哈夫曼编码表长度 */
306    HuffFile[4]=(len&0xff00)>>8;
307    HuffFile[5]=len&0x00ff;
308    
309    
310
311    {
312        for(int i=0,j=0;i!=len;i++,j=0)
313        {
314
315            for(;Huffman[i][j]!=2;j++);
316
317            HuffFile[3+i+2+1]=j;
318            HuffFile[3+i+2+2*len+1]=HuffmanList[i];
319
320
321            for(int k=0;k!=j;k++)
322            {
323
324                cout<<Huffman[i][k];
325                HuffFile[3+i+2+len+1]|=(Huffman[i][k]<<(j-1-k));
326
327            }

328            cout<<":"<<HuffmanList[i]<<endl;
329
330
331        }

332    }

333
334
335    
336    unsigned int packFileLen=(HuffFile[0]<<24)|(HuffFile[1]<<16)|(HuffFile[2]<<8)|(HuffFile[3]<<0);
337    unsigned int packCodeLen=((unsigned int)HuffFile[4]<<8)|((unsigned int)HuffFile[5]);
338
339
340    {
341        for(int i=0,j=0;i!=(packFileLen);i++)
342        {
343            for(int k=0;k!=packCodeLen;k++)
344            {
345
346                unsigned char l=HuffFile[3+2+k+1],d=j%8,b1=HuffFile[3+j/8+2+packCodeLen*3+1],b2=HuffFile[3+j/8+1+2+packCodeLen*3+1];
347
348                unsigned char c=HuffFile[3+packCodeLen+2+k+1];
349
350                if(compareBits(b1,b2,c,l,d))
351                {
352
353                    j+=HuffFile[3+2+k+1];
354
355                    GetFile[i]=HuffFile[3+2+packCodeLen*2+k+1];
356                    cout<<GetFile[i];
357
358                    break;
359
360                }

361            }

362
363        }

364    }

365
366    {
367        cout<<"哈夫曼压缩后二进制序列:"<<endl;
368        for(int i=0;i!=h;i++)
369        {
370            cout<<code[i];
371            if((i+1)%8==0)
372                cout<<" ";
373        }

374    }

375    cout<<endl;
376
377    {
378    cout<<"哈夫曼压缩打包假定文件格式二进制的文本体现:";
379    cout<<endl;
380    for(int i=0;i!=packFileLen+packCodeLen*3;i++)
381    {
382        cout<<HuffFile[i];
383    }

384    cout<<endl;
385    }

386
387    cout<<"原字节数为:"<<fileLen<<endl;
388    cout<<"压缩后字节数为:"<<(h)/8+1<<endl;
389    cout<<"压缩率为"<<((h/8.0+1)/fileLen)*100<<"%"<<endl;
390
391
392    {
393
394        cout<<"字符串字节数为:"<<(packFileLen)<<endl;
395        cout<<"字符串解压序列为:";
396        unsigned char buffer[MAX_FILE];
397        int i;
398        for(i=0;i!=(packFileLen);i++)
399        {
400            buffer[i]=GetFile[i];
401        }

402        buffer[i]=0;
403        cout<<buffer<<endl;
404
405
406
407
408        cout<<endl;
409
410    }

411    return 1;
412}




执行的case如下:

 1 请输入要压缩的字符串:I ask the indulgence of the children who may read  this  boo
 2 for  dedicating it to a grown - up.  I have a serious reason: he  is  the best frie
 3 nd I have  in  the world. I have another reason:  this  grown - up understands everyth
 4 ing
 5 even books about children. I have a third reason: he lives  in  France where he  is
 6  hungry and cold. He needs cheering up.$
 7 哈夫曼编码个数: 30
 8 哈夫曼编码序列:
 9 0000 :t
10 00010000 :F
11 00010001 :H
12 00010010 : < 回车 >
13 00010011 :m
14 000101 :b
15 00011 :u
16 0010 :s
17 0011 :o
18 0100 :i
19 0101 :a
20 011 :e
21 100000 :I
22 100001 :.
23 1000100 : -
24 1000101 ::
25 100011 :w
26 1001 :r
27 1010 :h
28 1011 :n
29 1100000 :f
30 1100001 :y
31 1100010 :k
32 1100011 :p
33 110010 :l
34 110011 :g
35 110100 :c
36 110101 :v
37 11011 :d
38 111 :
39 I ask the indulgence of the children who may read  this  book  for  dedicating it to
40  a grown - up.  I have a serious reason: he  is  the best friend I have  in  the world
41 . I have another reason:  this  grown - up understands everything
42 even books about children. I have a third reason: he lives  in  France where he  is
43  hungry and cold. He needs cheering up.哈夫曼压缩后二进制序列:
44 10000011   10101001   01100010   11100001   01001111   10100101   11101100   01111001   01100110
45   11101111   01000111   11001111   00000111   00001010   01111111   01001010   01001100   1011011
46 1   00101110   11111100   01110100   01111100   01001101   01110000   11111001   01101011   101111
47 10   00010100   10000101   11000101   00110011   11000101   11110000   00011100   11111101   10111
48 101   10100110   10001010   00001001   01111001   11110100   00001110   00000111   11010111   1110
49 0111   00100111   00011101   11000100   00011110   00111000   01111111   10000011   11010010   111
50 01010   11111010   11110010   01110010   10000110   00110010   11110010   11010100   10001110   11
51 100010   11111010   01111101   00001011   10000101   00111110   00101011   00100000   11111000   0
52 0100101   00011101   11101111   11000001   11101001   01110101   01111101   00101111   10000101
53 00111111   00011001   11001110   01011011   10000111   11000001   11101001   01110101   01111101
54   01101100   11000010   10011100   11111001   01101010   01000111   01110001   01111000   0101001
55 0   00010111   11001110   01001110   00111011   10001000   00111100   01111100   01110111   101101
56 11   00100100   00001011   01111011   00101110   11110101   01110011   10000100   00101001   00101
57 111   00110001   00100111   10101011   10111110   00101001   10011110   00100010   11101010   0010
58 1001   10001100   00111110   10010100   10011001   01101110   01011101   11000011   11100000   111
59 10100   10111010   10111110   10111100   00101001   00100111   01111110   01011010   10010001   11
60 011100   01011111   01001111   11100100   10011010   10110010   11101001   01111100   01000010   0
61 1010110   11110100   01111110   00111010   01110010   11111101   00111110   10000101   11101000
62 01110111   10011100   11100001   11101011   01111011   11111010   00011110   01011011   10000111
63   10001000   10111111   01101101   11101100   10111110   10010100   11011100   10100101   1110011
64 1   11000111   10001110   0001
65 哈夫曼压缩打包假定文件格式二进制的文本体现:
66
67 
68
69 (此处代码被截断,无法copy到剪贴板)
70
71 原字节数为: 341
72 压缩后字节数为: 181
73 压缩率为53. 2258 %
74 字符串字节数为: 341
75 字符串解压序列为:I ask the indulgence of the children who may read  this  book  for
76  dedicating it to a grown - up.  I have a serious reason: he  is  the best friend I
77 have  in  the world. I have another reason:  this  grown - up understands everything
78 even books about children. I have a third reason: he lives  in  France where he  is
79  hungry and cold. He needs cheering up.
80




写到此处,我突然发现在进行位处理的时候,我并没有用一个更简化的方法(100行内可以解决)- -bnr ,好吧 下次写一个通用的压缩软件。