利用哈夫曼編碼進行檔案壓縮(原創)過程大概是這樣的。
壓縮過程:讀入檔案,統計字元:在我的程式裡面是以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 ;
}