基于LZ77算法的文件压缩
生活随笔
收集整理的這篇文章主要介紹了
基于LZ77算法的文件压缩
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
基于Huffman算法和LZ77算法的文件壓縮(五)
基于Huffman算法和LZ77算法的文件壓縮(四)已經講解LZ77算法到基本原理和壓縮過程。
本文詳細講解文件壓縮過程當中的問題
一、文件壓縮的流程
上述在壓縮中用到的理論知識介紹完成之后,就可以開始LZ77的壓縮了,LZ77的壓縮過程具體如下:
a.計算哈希地址,將該字符串首字符在窗口中的位置插入到哈希桶中,并返回該桶的狀態matchHead
b.根據matchHead檢測是否找到匹配
- 如果matchHead等于0,未找到匹配,表示該三個字符在前文中沒有出現過,將該當前字符 作為源字符寫到壓縮文件中
6 . 如果窗口中的數據小于MIN_LOOKAHEAD時,將右窗口中數據搬移到左窗口,從文件中新讀取一個窗口的數據放置到右窗,更新哈希表,繼續壓縮,直到壓縮結束。
二、 壓縮格式數據保存
壓縮格式分三個文件保存:
上面的信息之前已經講解過,接下來就對每個步驟講解。
三、問題詳細解釋
- 1個字節的存儲范圍是[0,255],255理論上來說已經比較長(正常文件的字符匹配長度可能還達不到255),如果超過255就需要用2個字節存儲,且真正字符匹配長度超過255的很少,所以用2個字節存儲字符匹配長度對壓縮率有一定的影響,即用一個字節存儲即可。
- 要知道匹配字符的距離有多大就需要緩沖區的大小(即滑動窗口的大小),緩沖區越大,那么查找緩沖區和先行緩沖區就會越大,匹配字符的距離就會越長,前面說過,緩沖區不會無限大,那么緩沖區具體應該有多大呢??----- 64K大小
- 理論上只要是緩沖功能區當中壓縮過的數據都因該是查找緩沖區,如下面這種情況:
- 那么這種情況的緩沖區的大小肯定是大于32K的,找到重復的概率會變大。但是我們不可能讓匹配距離太遠,且重復一般都有局部性(重復一般不會距離太遠),如為了匹配3個字節而遍歷55K的查找緩沖區是無意義的,反而影響文件的壓縮效率。真正的匹配范圍是不會超過WSIZE的,即32K。
- 所以用2個字節來保存字符匹配的距離
- 經過前面的分析,長度距離對共占用1 + 2 = 3個字節,所以重復字符串的長度至少為3個字符才開始匹配,小于3個就去匹配就會使壓縮文件變大,那么你在搞什么東東???
- 定義幾個變量來進行標識:
答案就是哈希。
5.哈希表要給多大???
- 哈希表的大小取決于哈希函數計算出來的哈希地址,因為哈希表至少能夠包含到所有的哈希地址。
- 哈希函數如下,其原理是用3個字符的每個字符的比特位進行操作,那么哈希地址的范圍就是3個字符的比特位共有多少種組合,即256 * 256 * 256 = 2^24種組合,
- 而哈希表當中 每個位置保存的是首字符在緩沖區當中的下標(緩沖區的大小是64K == 65535)(其實只用到[0,32767],因為只在查找緩沖區中進行),所以還需要2個字節。
- 所以最后哈希表的大小是: 2 ^ 24 * 2 = 2 ^ 25 == 32M空間,
- 32M空間是非常大的,每次進行壓縮都要維護32M空間是非常耗時的,所以人為規定哈希表當中位置的個數為:2^15 == 32K,整個哈希表的大小為2 ^ 15 * 2 = 2 ^ 16,但是注意后面會改造哈希表,哈希表的大小就會變,就不是2 ^ 16 大小了。
- 那么這樣給哈希表的大小帶來的問題是:哈希地址多于哈希表的位置數,那么就一定會發生哈希沖突,發生哈希沖突怎么解決呢?你不能不管不顧吧,那就改造哈希表格。
6.哈希表的結構
- prev指針專門用來解決哈希沖突的
- head的作用是用來保存記錄匹配鏈的頭。因為數據在哈希表當中的保存相當于一個鏈,發生哈希沖突的元素都在這個鏈上。
- prev和head的大小都為WSIZE的原因是為了和后面統一。
- 發生哈希沖突的解決過程:
- 一定要理解哈希沖突的解決過程,每個位置的意義
7.哈希表的越界問題
- 隨著壓縮的進行,可定會進行到大于WSIZE處,即圖中的start處,那么在進行往哈希表當中插入元素時執行_prev[pos] = _head[hashAddr]就會越界,既然越界就需要解決。
- 越界的解決方式:pos & WMASK,保證結果在WSIZE的范圍內
- 這種解決越界問題帶來的后果是什么?可能會出現死循環
8.用&WMASK來防止越界可能帶來的結果
- 可能會出現死循環的情況
- 如下圖,&完的結果可能為10,那么執行_prev[10] = _head[hashAddr]后就會在_prev內部形成一個環。
- 假設還有一個相同字符串的匹配到來,就會執行machHead = _head[hashAddr];machHead = _prev[machHead]...那么根據下圖,他就會一直在_prev內部轉圈,形成死循環。
- 這種死循環的解決方式:人為規定最多找匹配次數為255。原因有兩種:
- 1.如果為了找匹配進行了超過255次的查找過程,那么就會影響壓縮文件的效率,不劃算。
- 2.粗暴的解決這種死循環問題,讓他直接break。
9.哈希表的構造
- 哈希表的構造過程是和在哈希表當中找匹配是同步進行的。
- 哈希表中保存的內容是先行緩沖區當中取出來匹配到3個字符的首字符在整個緩沖區的下標
10.先行緩沖區當中剩余字符的最小個數是多少?
- 如下圖,如果先行緩沖區當中剩余的字符太少還去進行匹配,那么就無法達到最優的匹配結果,所以需要設置一個變量來進行標記。
- 定義MIN_LOOKAHEAD = MAX_MACTH + MIN_MACTH + 1;
- MIN_LOOKAHEAD代表先行緩沖區當中剩余字符的最小個數
- MAX_MACTH代表字符串的最大匹配長度,此處的目的是為了滿足當前字符串能夠得到最優的匹配結果
- MIN_MACTH 代表字符串的最小的匹配長度,MIN_MACTH + 1此處的目的是是保證還可以進行下一次匹配
- 當先行緩沖區當中的數據小于MIN_LOOKAHEAD則不進行匹配
- 定義MAX_DIST = WSIZE - MIN_LOOKAHEAD = 32K - 258 - 3 - 1 ;MAX_DIST 代表最大的字符串匹配距離,這個就很容易理解,因為在下面11問中的窗口數據的挪移過程中,查找緩沖區當中只剩下WSIZE - MIN_LOOKAHEAD這么多了!!!
11.怎么保證先行緩沖區當中的數據大于等于MIN_LOOKAHEAD?
給個圖,解釋每個步驟:
- 將右窗WSIZE2當中的數據導入到左窗WSIZE1當中
- 從待壓縮文件當中再讀取一個WSIZE個字符到右窗WSIZE2,更新當前壓縮指針的位置(假設當前壓縮到start處,把start -= WSIZE;)
- 必須要更新哈希表,因為整個數據的位置已經挪動,
注意注意注意,如果待壓縮文件本來的大小就小于WSIZE的情況。此時再去文件當中讀取WSIZE個數據到右窗WSIZE2當中就會報錯,使用的時候需要另行判斷。
12.怎么更新哈希表?
- 把prev和head當中大于WSIZE的數據都減掉WSIZE,小于WSIZE的數據直接置為0即可。
到這里整個過程的細節都已經解釋完畢,終于可以擼代碼了!!!
請看下節~~~
總結
以上是生活随笔為你收集整理的基于LZ77算法的文件压缩的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于LZ77算法的文件压缩铺垫
- 下一篇: 基于LZ77算法的文件压缩收尾