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檔案也稱為文字檔案,這種檔案在磁碟中存放時
<3> 在這裡以文字形式讀取壓縮檔案,有可能提前遇到檔案結束標誌。二進位制形式是讀取二進位制編碼,如果以文字形式讀取的話,回車和換行會被當成一個字元'\n',而二進位制形式則會認為它是兩個字元即'\r'回車、'\n'換行;如果在文字形式中遇到0x1B的話,文字形式會認為這是文字結束符,而二進位制模型則不會對它產生處理,所以這裡需要以"rb","wb"的方式才能正確讀寫檔案。
1.3) 由於ASCII碼字元一共
說明: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上: