文件壓縮項目

壓縮緣由
1.文件太大,節省空間
2.提升數據在網絡上傳輸的效率
3.對數據起到保護做用---加密
文件壓縮類型
無損壓縮:源文件被壓縮以後,能夠經過解壓縮還原成與源文件相同的格式
有損壓縮:源文件被壓縮以後,解壓縮沒法還原成與源文件相同,但識別其內容沒有影響,多用於語音,圖片,視頻壓縮
基於Huffman樹的壓縮如何實現
首先先對文件進行LZ77壓縮(也就是基於字符層面的壓縮),而後在LZ77壓縮文件的基礎上再進行Huffman壓縮
第一,什麼是LZ77壓縮
LZ77是基於字節的通用壓縮算法,它的原理就是將源文件中的重複字節(即在前文中出現的重複字節)使用(長度,距離)對來表示,例如
mnoabczxyuvwabc123456abczxydefgh
壓縮以後:mnoabczxyuvm93123456186defgh。
爲了記錄壓縮以後的數據究竟是文件數據仍是(長度距離)對,而又加了一個信息文件
信息文件(比特位):0表明數據,1表明的是長度距離對
LZ77壓縮原理
1.構建一個64k連續空間窗口(文件數據讀取的緩衝區),窗口中分爲查找緩衝區和先行緩衝區,先行緩衝區中數據壓縮時,經過哈希表找出最長匹配串即距離,若是沒有在前文中找到重複則把原字節放入壓縮文件中,若是找到重複,則把長度距離對放入壓縮文件中
文件壓縮項目
2.構建哈希表,因爲壓縮時寫長度距離對時有三個字節,因此低於三字節的數據不壓縮,因此哈希表構建須要256256256,即2^24個空間個數,也就是32M,可是因爲空間太大,以及隨着壓縮不斷進行,哈希表中內容不斷更新,維護起來特別耗時,最終給哈希表的個數爲2^15個空間,在這種「狼多肉少」的狀況下,爲了不哈希衝突,採用了64k的連續空間來維護哈希表,右窗head空間全置爲0
文件壓縮項目
3.經過讀取文件以三個字節爲一組填入哈希表中,若是遇到單組字節,直接填入,若是遇到重複字節,把右窗中的數值填入左窗,即
文件壓縮項目算法

文件壓縮項目

4.爲了讓解壓縮的時候能夠識別究竟是數據文件仍是長度距離對,而又加了一個信息文件
信息文件(比特位):0表明數據,1表明的是長度距離對 對每個字節壓縮的時候,須要同時判斷壓縮字節是否爲長度距離對,按比特位壓入編程

第二,什麼是Huffman樹
給定N個權值做爲N個葉子結點,構造一棵二叉樹,若該樹的帶權路徑長度達到最小,稱這樣的二叉樹爲最優二叉樹,也稱爲哈夫曼樹。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。
例如:給定權值爲1(A),3(B),5(C),7(D)四個節點,構建Huffman樹
文件壓縮項目
Huffman壓縮原理--基於Huffman編碼
以字符串中每一個字符出現的次數爲權值構建Huffman樹
從根節點開始,左分支爲0,右分支爲1,如上圖
全部權值節點都在葉子節點位置,遍歷每條到葉子節點的路徑獲取字符的編碼數組

舉個栗子:ABBBCCCCCDDDDDDD
Huffman編碼:
A:100
B:101
C:11
D:0

原理就是這麼簡單,一個字符佔一個字節,如今用二進制編碼代替以後,一個字符只佔三位,也就是說一個字節能夠表示兩三個字符,因此說一次壓縮,就會節省不少字節,也就起到了壓縮的做用。
Huffman壓縮中最重要的是三點
建立Huffman樹:網絡

1 先用權值建立n棵只有根節點的二叉樹森林【意思是先建立n個節點】
2 選取根節點權值最小的二叉樹構建新的二叉樹【建小堆,新二叉樹根節點權值爲左右子樹的根節點權值之和】【用到了priority_queue優先級隊列】
3 刪除使用的兩棵根節點權值較小的二叉樹
4 將新建立的二叉樹添加到二叉樹森林中
接下來2-4循環繼續,直到二叉樹森林中只有一棵二叉樹則Huffman樹建立成

文件壓縮過程:ide

1讀取源文件,讀取源文件中每一個字符出現的次數
2 以每一個字符出現的次數做爲權值,建立huffman樹:小堆--優先級隊列
3 經過huffman樹找每一個字符對應的編碼
4 用每一個字符的新編碼從新對源文件進行改寫【翻譯的過程】

文件解壓縮的過程:函數

1 從壓縮文件中獲取源文件的後綴
2 從壓縮文件中獲取字符次數的總行數
3 獲取每一個字符出現的次數
4 重建huffman樹
5 解壓壓縮數據
    a. 從壓縮文件中讀取一個字節的獲取壓縮數據ch
    b. 從根節點開始,按照ch的8個比特位信息從高到低遍歷huffman樹:該比特位是0,取當前節點的左孩子,不然取右孩子,直到遍歷到葉子節點位置,該字符就被解析成功,將解壓出的字符寫入文件,若是在遍歷huffman過程當中,8個比特位已經比較完畢尚未到達葉子節點,從a開始執行
    c. 重複以上過程,直到全部的數據解析完畢。

第三,對LZ77和Huffman壓縮進行整合
文件壓縮和解壓縮過程:LZ77壓縮-->Huffman壓縮(LZ77基礎上)-->Huffman解壓縮-->LZ77解壓縮
1.整合過程當中,會出現臨時文件(LZ77壓縮信息文件,LZ77壓縮後的壓縮文件,Huffman解壓縮以後的文件),因此須要對這些文件進行刪除:remove函數
2.壓縮文件和解壓縮過程當中,LZ77和Huffman有交替過程,因此在調用函數時的傳參過程須要封裝測試

寫代碼當中碰到的一些主要的問題,我將這些總結起來:
LZ77過程:
編程前:編碼

首先知識層面的欠缺,查閱了好多資料加密

編程時:翻譯

1.當壓縮文件壓縮至右窗的時候,爲了防止哈希表中prev(數組)越界,須要&HASH_SIZE-1,可是一旦這樣作就會出現匹配鏈成環的現象

解決方案:匹配次數不超過255次

2.壓縮文件開頭內容須要對文件後綴進行寫入,在寫入時,信息文件並無填入,因此解壓縮時,文件出現亂碼

解決方案:解壓縮時,文件指針對壓縮文件頭部進行讀取,但不對標記信息進行讀取

3.對於一個文件中,在解壓縮時會中文大部分出現亂碼,英文有少部分

解決方案:解壓縮時,遇到距離長度對的時候,每解壓縮一個字節,都須要刷新一次緩衝區,由於後面的數據中可能有這次解壓的字節

Huffman過程:

1.編譯的時候:
剛開始寫的時候測試發現若是壓縮文件中出現了中文,程序就會崩潰,最後發現是數組越界的錯誤,由於若是隻是字符,它的範圍是-128~127,程序中使用char類型爲數組下標(0~127),因此字符沒有問題. 可是漢字的編碼是兩個字節,因此可能會出現越界,

解決方法:就是將char類型強轉爲unsigned char,下標可表示範圍爲0~255.

2.解壓縮的時候
有些特殊字符在處理須要注意一下,好比'\n',個人程序中Getline()函數就是讀取一行字符,可是如果該字符自己就是一個'\n'呢? 這就很是的棘手了. 由於解壓縮以後出現了亂碼

解決方法:讀取壓縮文件時若讀到了'\n',則說明該字符就是'\n',應該繼續讀取它的次數

3.運行的時候:
發現文件篇幅很長的時候,只能壓縮和解壓縮一部分,是由於字符長度的設定過小

解決方法:_count長度設爲unsigned long long類型

測試方面:
1.對於txt文件壓縮率還能夠,可是對於音頻和視頻方面壓縮率較低
2.爲了壓縮率,時間複雜度優勢高

壓縮率

文件類型 源文件大小 壓縮後大小 壓縮率
txt文檔 27.8KB 12.5KB 0.45
音頻文件 29.8 MB 31.9MB 1.07
視頻文件 20.7MB 22.1MB 1.06
相關文章
相關標籤/搜索