利用哈夫曼编码进行文件压缩(原创)


过程大概是这样的。
压缩过程:读入文件,统计字符:在我的程序里面是以BYTE类型而不是char类型,这样可以压缩除文本文件之外的其他文件。然后建立哈夫曼树。存入压缩文件的时候我先存的原文件总的BYTE数,用了4个字节去存。之所以这么做,是因为文本在被压缩后,最后一部分不一定能占满一个字节,但是存进去的时候必须要按一个字节存进去,这样就会造成解压的时候产生多余的信息。然后将哈夫曼树对应的静态链表存进去,我用1和0分别表示他的左右孩子指向的是其他的中间结点还是叶子(文件中的字符)。然后按照哈夫曼编码把源文件的内容放到了压缩文件。在处理这个静态链表的时候我犯傻了,应该把指示左右孩子的tag用位操作把他们都单独放在一处,而不是一个tag就是一个BYTE,这样很浪费空间的。在建立哈弗曼树的时候需注意文件只有一种字符的情况。我用的是把头节点的左右孩子都指向这个字符。还有一个办法是把这个字符的数量存下来,就压缩数量和字符,不过这样的解码方式不一样。我比较懒就用的第一种方式。
解压过程:根据哈夫曼编码的静态链表进行解压。对于编码来说,0表示走向左孩子,1表示走向右孩子。比如说我的a的哈夫曼编码是10,则我从静态链表的头开始找起,先是“1”,那我选择右孩子,然后是“0”,他的左孩子就是'a'。这样就能解压了。
核心代码如下:

typedef struct st 
  
{ 
  
 short Letter ; 
  
 int  Frequent ; 
  
 st   *left, *right ; 
  
} HuffmanNode, *pHuffman ; 
  
 

   typedef struct CodeLList 
  
{ 
  
 HuffmanNode Node ; 
  
 BYTE left, right ;          
  
 BYTE lefttag, righttag ; // if tag == 0, means that it is a index. 
  
}CodeLList ; 
  
 
 
  
    case ID_COMPRESSBUTTON : 
  
    { 
  
     used = 0 ; 
  
     HuffmanNode temp ; 
  
     char buffer[258] ; 
  
 

        Node = new pHuffman[256] ; 
  
     FileByteLen = 0 ; 
  
 

        for ( i = 0 ; i < 256 ; i++ ) 
  
     { 
  
      ElementCount[i] = 0 ; 
  
     } 
  
     
  
     GetWindowText( hEditFile, szFileName, 50 ) ; 
  
     if ( szFileName[0] == '/0' ) 
  
     { 
  
      MessageBox( hwnd, "请输入文件名", "Warning!", 
  
       MB_OK | MB_ICONWARNING ) ; 
  
      return 0 ; 
  
     } 
  
     else 
  
      fp = fopen( szFileName, "rb" ) ; 
  
     if ( !fp ) 
  
     { 
  
      MessageBox( hwnd, TEXT( "找不到该文件" ), TEXT( "Warning!" ), 
  
       MB_OK | MB_ICONWARNING ) ; 
  
      return 0 ; 
  
     } 
  
 

        while ( !feof( fp ) ) 
  
     { 
  
      fread( &character, sizeof( BYTE ), 1, fp ) ; 
  
      if ( feof( fp ) ) 
  
       break ; 
  
      ElementCount[character]++ ; 
  
      FileByteLen++ ; 
  
     } 
  
 
 
  
     for ( i = 0 ; i < 256 ; i++ ) 
  
     { 
  
      if ( ElementCount[i] > 0 ) 
  
      { 
  
       Node[used] = new HuffmanNode ; 
  
       Node[used]->Frequent = ElementCount[i] ; 
  
       Node[used]->Letter = i ; 
  
       Node[used]->left = Node[used]->right = NULL ; 
  
       used++ ; 
  
      } 
  
     } 
  
 

        // 从小到大排序 
  
     for ( i = 0 ; i < used - 1 ; i++ ) 
  
      for ( j = i + 1 ; j < used ; j++ ) 
  
      { 
  
       if ( Node[i] > Node[j] ) 
  
       { 
  
        temp = *Node[i] ; 
  
        *Node[i] = *Node[j] ; 
  
        *Node[j] = temp ; 
  
       } 
  
      } 
  
 

        // 生成哈夫曼树 
  
     begin = 0 ; 
  
     while ( begin < used - 1 ) 
  
     { 
  
      p = new HuffmanNode ; 
  
      p->Letter = -1 ; 
  
      p->Frequent = Node[begin]->Frequent + Node[begin + 1]->Frequent ; 
  
      p->left = Node[begin] ; 
  
      p->right = Node[begin + 1] ; 
  
 

         for ( i = begin + 1 ; i < used - 1; i++ ) 
  
      { 
  
       if ( Node[i + 1]->Frequent < p->Frequent ) 
  
        Node[i] = Node[i + 1] ; 
  
       else 
  
        break ; 
  
      } 
  
 

         if ( begin + 2 >= used ) 
  
       Node[begin + 1] = p ; 
  
      else 
  
       Node[i] = p ; 
  
       
  
      begin++ ; 
  
     } 
  
      
  
     if ( used > 1 ) 
  
     { 
  
      Head = p ; 
  
      
  
      BuildCode( Head, buffer, 0 ) ; 
  
     } 
  
     else 
  
     { 
  
      Code[Node[0]->Letter][0] = '0' ; 
  
      Code[Node[0]->Letter][1] = '/0' ; 
  
     } 
  
      
  
     len = strlen( szFileName ) ; 
  
     szFileName[len] = '.' ; 
  
     szFileName[len + 1] = 's' ; 
  
     szFileName[len + 2] = 'l' ; 
  
     szFileName[len + 3] = '/0' ; 
  
 

        fpDest = fopen( szFileName, "wb+" ) ; 
  
      
  
     // 开始写入压缩文件 
  
     fwrite( &FileByteLen, sizeof( int ), 1, fpDest ) ; 
  
     // 哈夫曼码表 
  
     if ( used > 1 ) // 对只有一个字符种类的文章要特殊处理。 
  
      count = ConvertTreeList( Head ) ; 
  
     else 
  
     { 
  
      count = 1 ; 
  
      CodeLinkList[0].left = Node[0]->Letter ; 
  
      CodeLinkList[0].right = Node[0]->Letter ; 
  
      CodeLinkList[0].righttag = CodeLinkList[0].lefttag = 1 ; 
  
     } 
  
     fwrite( &count, sizeof( BYTE ), 1, fpDest ) ; 
  
     for ( i = 0 ; i < count ; i++ ) 
  
     { 
  
      fwrite( &CodeLinkList[i].left, sizeof( BYTE ), 1, fpDest ) ; 
  
      fwrite( &CodeLinkList[i].lefttag, sizeof( BYTE ), 1, fpDest) ; 
  
      fwrite( &CodeLinkList[i].right, 1, sizeof( BYTE ), fpDest ) ; 
  
      fwrite( &CodeLinkList[i].righttag, sizeof( BYTE ), 1, fpDest) ; 
  
     } 
  
      
  
     // 编码 
  
     bitcount = 0 ; 
  
     codebyte = 0 ; 
  
     fseek( fp, 0, SEEK_SET ) ; 
  
     while ( !feof( fp ) ) 
  
     { 
  
      character = fgetc( fp ) ; 
  
      if ( character < 0 ) 
  
       break ; 
  
      len = strlen( Code[character] ) ; 
  
      for ( i = 0 ; i < len ; i++ ) 
  
      { 
  
       if ( bitcount == 8 ) 
  
       { 
  
        fwrite( &codebyte, sizeof( BYTE ), 1, fpDest ) ; 
  
        codebyte = 0 ; 
  
        bitcount = 0 ; 
  
       } 
  
       codebyte *= 2 ; 
  
       if ( Code[character][i] == '1' ) 
  
        codebyte++ ; 
  
       bitcount++ ; 
  
      } 
  
     } 
  
 

        if ( bitcount <  8 ) 
  
     { 
  
      while ( bitcount < 8 ) 
  
      { 
  
       codebyte *= 2 ; 
  
       bitcount++ ; 
  
      } 
  
 

         fwrite( &codebyte, sizeof( BYTE ), 1, fpDest ) ; 
  
     } 
  
      
  
     fclose( fp ) ; 
  
     fclose( fpDest ) ; 
  
     return 0 ; 
  
    } break ; 
  
 

       case ID_EXPANDBUTTON : 
  
    { 
  
     BYTE currentindex ; 
  
     bool ByteContent[8] ; 
  
 

        GetWindowText( hEditFile, szFileName, 50 ) ; 
  
     len = strlen( szFileName ) ; 
  
     if ( szFileName[0] == '/0' ) 
  
     { 
  
      MessageBox( hwnd, "请输入文件名", "Warning!", 
  
       MB_OK | MB_ICONWARNING ) ; 
  
      return 0 ; 
  
     } 
  
     else if ( szFileName[len - 1] != 'l' || 
  
      szFileName[len - 2] != 's' || 
  
      szFileName[len - 3] != '.' || len == 3) 
  
     { 
  
      MessageBox( hwnd, "文件类型不对", "Warning!", 
  
       MB_OK | MB_ICONWARNING ) ; 
  
      return 0 ; 
  
     } 
  
     else 
  
      fp = fopen( szFileName, "rb" ) ; 
  
     if ( !fp ) 
  
     { 
  
      MessageBox( hwnd, TEXT( "找不到该文件" ), TEXT( "Warning!" ), 
  
       MB_OK | MB_ICONWARNING ) ; 
  
      return 0 ; 
  
     } 
  
 

        fread( &FileByteLen, sizeof( int ), 1, fp ) ; 
  
     fread( &count, sizeof( BYTE ), 1, fp ) ; 
  
     for ( i = 0 ; i < count ; i++ ) 
  
     { 
  
      fread( &CodeLinkList[i].left, sizeof( BYTE ), 1, fp ) ; 
  
      fread( &CodeLinkList[i].lefttag, sizeof( BYTE ), 1, fp ) ; 
  
      fread( &CodeLinkList[i].right, sizeof( BYTE ), 1, fp ) ; 
  
      fread( &CodeLinkList[i].righttag, sizeof( BYTE ), 1, fp ) ; 
  
     } 
  
      
  
     // 得到解压后的文件名 
  
     szFileName[len - 3] = '/0' ; 
  
     fpDest = fopen( szFileName, "wb+" ) ; 
  
      
  
     currentindex = 0 ; 
  
      
  
     while ( !feof( fp ) && FileByteLen ) 
  
     { 
  
      fread( &codebyte, 1, 1, fp ) ; 
  
      for ( i = 0 ; i < 8 ; i++ ) 
  
      { 
  
       ByteContent[7 - i] = codebyte & 1 ; 
  
       codebyte /= 2 ; 
  
      } 
  
       
  
      for ( i = 0 ; i < 8 ; i++ ) 
  
      { 
  
       if ( !ByteContent[i] ) // 左子树 
  
       { 
  
        if ( CodeLinkList[currentindex].lefttag ) 
  
        { 
  
         //fwrite( &CodeLinkList[currentindex].left, sizeof( 
  
 

   BYTE ), 1, fpDest ) ; 
  
         fputc( CodeLinkList[currentindex].left, fpDest ) ; 
  
         FileByteLen-- ; 
  
         currentindex = 0 ; 
  
        } 
  
        else 
  
         currentindex = CodeLinkList[currentindex].left ; 
  
       } 
  
       else 
  
       { 
  
        if ( CodeLinkList[currentindex].righttag ) 
  
        { 
  
         //fwrite( &CodeLinkList[currentindex].right, sizeof( 
  
 

   BYTE ), 1, fpDest ) ; 
  
         fputc( CodeLinkList[currentindex].right, fpDest ) ; 
  
          
  
         FileByteLen-- ; 
  
         currentindex = 0 ; 
  
        } 
  
        else 
  
         currentindex = CodeLinkList[currentindex].right ; 
  
       } 
  
 

          if ( !FileByteLen ) 
  
        break ; 
  
      } 
  
     } 
  
      
  
     fclose( fp ) ; 
  
     fclose( fpDest ) ; 
  
     return 0 ; 
  
    } break ; 
  
   }