1. 程式人生 > >huffman演算法實現檔案的壓縮與解壓

huffman演算法實現檔案的壓縮與解壓

本文采用哈夫曼編碼的方式進行檔案的壓縮和解壓縮,主要原理是通過huffman編碼來表示字元,出現次數多的編碼短,出現次數少的編碼長,這樣整體而言,所需的總的bit位是減少的。但是當大部分字元出現的頻率都差不多時,huffman壓縮的壓縮率就會很低。

1.利用

哈夫曼編碼壓縮檔案,主要思想:

(1).統計出檔案中各個字元出現的次數;

(2).構建huffmanTree,生成每個字元對應的編碼,然後將編碼寫入壓縮檔案中;

(3).編寫配置檔案,因為在壓縮後是找不到原檔案的,所以需要藉助配置檔案來記錄檔案出現的字元以及字元出現的次數,方便解壓縮;

(4).檔案的解壓縮實際上就是將壓縮檔案翻譯過來儲存到解壓縮檔案中,需要使用壓縮過程中生成的配置檔案配合完成。

2.下面具體介紹檔案的壓縮和解壓縮步驟.

首先是檔案壓縮:
1) 統計檔案中所有字元出現的次數,需要明白以下幾點:

     1.1)在文字檔案中,資料是以字元的ASCII碼形式存放,ASCII碼的範圍是0-255,所以檔案壓縮中以256的陣列作為底層資料結構,其中資料型別為CharInfo,包括字元,字元出現次數以及huffman編碼;

     1.2)在讀寫檔案時要用二進位制形式,以”r”,”w”“rb”,“wb”來進行讀寫,它們主要的區別在哪呢?

   <1>從檔案編碼的方式來看,檔案可分為ASCII碼檔案和二進位制碼檔案兩種。ASCII檔案也稱為文字檔案,這種檔案在磁碟中存放時

每個字元對應一個位元組,用於存放對應的ASCII碼;   <2>二進位制檔案是按二進位制的編碼方式來存放檔案的。例如,5678的儲存形式為:00010110 00101110只佔二個節。

         <3> 在這裡以文字形式讀取壓縮檔案,有可能提前遇到檔案結束標誌。二進位制形式是讀取二進位制編碼,如果以文字形式讀取的話,回車和換行會被當成一個字元'\n',而二進位制形式則會認為它是兩個字元即'\r'回車、'\n'換行;如果在文字形式中遇到0x1B的話,文字形式會認為這是文字結束符,而二進位制模型則不會對它產生處理,所以這裡需要以"rb","wb"的方式才能正確讀寫檔案。

     1.3) 由於ASCII碼字元一共

256個,只有前128個字元可以顯示,定義字元變數時需要定義成unsigned char 型,在讀取檔案時,如果用while(ch!=EOF)來判斷,ch有可能讀不到檔案的結尾就提前結束,所以我們採用函式feof來代替檔案的結束標誌EOF.

       說明:EOF的16進製為0xFF(十進位制為-1),特用在文字檔案中,因為在文字檔案中資料是以ASCⅡ碼值的形式存放,普通字元的ASCⅡ碼的範圍是32到127(十進位制),與EOF不衝突,因此可以直接使用;但是在二進位制檔案中,資料有可能出現-1,因此不能用EOF來作為二進位制檔案的結束標誌,可以通過feof函式來判斷。也就是說feof這個函式可用於判斷檔案是否結束(包括文字檔案和二進位制檔案)。

2)構建huffman樹.

通過建一個小堆,將統計到count !=0的結點壓入堆中,從堆中取最小資料以及次小資料,將它們的和作為哈夫曼樹的權值節點,構建到huffman樹中 

         這裡說明一下,在構建huffman樹的時候,如果將256個字元全部構建進去,不僅會降低效率還將佔用很大空間,所以引入非法制的概念,只將次數不為0的節點構建到huffmanTree中去。

3)通過哈夫曼樹產生哈夫曼編碼;

    規則是:從根節點出發,往左走-->0 ,往右走-->1,遇到葉子節點的情況,就將它對應的huffman編碼寫入陣列中。

4)從原檔案得到字元,將對應的哈夫曼編碼每滿8個位元組就寫入壓縮檔案中,如果最後一個位元組不滿8位,用0填充;

5)編寫配置檔案.

  由於在解壓時往往是沒有原檔案的,而我們要解壓的話必須要知道這棵huffman樹,所以在壓縮的時候需要編寫一個配置檔案來儲存huffman樹的資訊(各個字元以及字元出現的次數)。在配置檔案裡面將:字元+字元出現的次數存在一行。在這裡要使用itoa這個函式將次數轉換成一個字串(string)型別儲存。

       在壓縮圖片或音訊的時候出現問題,主要是對fwrite和fputs的使用不明確.

        fwrite(line.c_str(),1,line.size(),finConfig);
       //fputs(line.c_str(),finConfig);

 fputs是文字形式寫入一個字串,遇到‘\0’就停止寫入; fwrite是二進位制寫,可以指定寫入多少個位元組.


2.檔案的解壓縮.

1)從配置檔案中得到各種字元出現的次數;

 1>讀配置檔案時空行的處理一定要注意,以及如何將字串中存放的次數轉換為數字,這裡使用atoi函式

使用string::substr(pos)函式提取字元出現的次數

   _str[ch]._count = atoi(line.substr(2).c_str());

 2>因為string底層是char*,而每一行的第一個字元是0—255,所以這一塊我們要進行處理,可以單獨讀取。

2)重新構建huffman樹;

3)在壓縮檔案裡面讀取一個字元,然後解析這個字元的每一位,只要遇到一個葉子結點,就代表還原了一個字元,這時候將字元寫到解壓縮檔案裡但是在這裡要注意,有可能最後的幾位編碼是我們補上去的,所以在這裡我們要以原檔案中字元出現的次數來控制解壓縮,根據huffman的性質可知,根節點的權重就是字元出現的次數。


對於以上的用Huffman演算法實現檔案的壓縮與解壓縮,其原始碼已託管至github上: