日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于LZ77算法的文件解压缩项目缺陷分析

發布時間:2024/4/11 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于LZ77算法的文件解压缩项目缺陷分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基于Huffman算法和LZ77算法的文件壓縮(七)

基于Huffman算法和LZ77算法的文件壓縮(六)已經講解完文件壓縮的過程,本文講解文件解壓縮的過程和大文件處理方式

一、解壓縮的流程

LZ77的解壓縮非常簡單:

  • 從文件1中讀取標記,并對該標記進行分析
  • 如果當前標記是0,表示原字符,從文件2中讀取一個字節,直接寫到解壓縮之后的文件中
  • 如果當前標記是1,表示遇到(距離,長度對),從文件3中讀取一個兩個字節表示距離,從文件1中讀取一個字節表示長度,構建(距離,長度)對,然后從解壓縮過的結果中找出匹配長度
  • 獲取下一個標記,直到所有的標記解析完。
  • 二、合并標記信息文件和壓縮數據文件

    • 開始解壓縮前我們需要合并壓縮數據文件和標記信息文件,那么合并到一起怎么區分壓縮數據和標記信息呢
    • 我們還要在合并后的壓縮文件當中寫入標記信息的總字節數和原數據文件的大小。

    • 注意合并完成后文件大小和標記信息大小的 獲取方式:
    //獲取源文件大小ULL fileSize = 0;fseek(fInF, 0 - sizeof(fileSize), SEEK_END);fread(&fileSize, sizeof(fileSize), 1, fInF);//獲取標記信息的大小size_t flagSize = 0;fseek(fInF, 0 - sizeof(fileSize) - sizeof(flagSize), SEEK_END);fread(&flagSize, sizeof(flagSize), 1, fInF);
    • 合并文件
    //合并文件 void LZ77::MergeFile(FILE* fOut, ULL fileSize) {//將壓縮數據文件和標記信息文件合并//1.讀取標記信息文件中內容,然后將結果寫入到壓縮文件中FILE* fInf = fopen("3.txt", "rb");size_t flagSize = 0;UCH* pReadbuff = new UCH[1024];while (true) {size_t rdSize = fread(pReadbuff, 1, 1024, fInf);if (rdSize == 0)break;//把標記信息寫到壓縮數據文件中fwrite(pReadbuff, 1, rdSize, fOut);//更新數據flagSize += rdSize;}//向壓縮數據文件中寫標記信息大小,方便解壓縮fwrite(&flagSize, sizeof(flagSize), 1, fOut);//向壓縮數據文件中寫文件數據大小,方便解壓縮fwrite(&fileSize, sizeof(fileSize), 1, fOut);delete[] pReadbuff;fclose(fInf); }
    • 注意讀取標記信息文件指針的起始位置
    //將讀取標記信息的文件指針移動到保存標記數據的起始位置fseek(fInF, 0 - sizeof(flagSize) - sizeof(fileSize) - flagSize, SEEK_END);
    • 合并完后,解壓縮的流程:


      三、開始解壓縮

    • 同樣還是先在標記文件當中讀取一個字節chFlag(其8個比特位的01就代表是原字符還是長度距離對),& 0x80來判斷高位01情況

    • 注意如果標記信息是1,就代表是長度距離對,把長度讀取出來需要 + 3。

    • 如果是長度距離信息,需要再來一個文件指針,從解壓縮文件末尾往前偏移距離個位置(即定位到匹配字符的開始),然后往后寫長度個字符到解壓縮文件

    • 注意注意在,操作系統為了提高讀取寫入的效率,是提供緩沖區的機制,如果你向文件當中寫入數據,其實是寫到緩沖區當中,緩沖區滿就才會往文件當中寫,而前面我們說過,解壓縮的文件是有兩個指針打開的(一個是寫壓縮數據,一個是定位匹配字符的開始位置),現在如果寫壓縮數據的文件指針把數據寫入到緩沖區并沒有寫入到文件,此時定位匹配字符開始位置的指針需要讀取長度個字符寫入到解壓縮文件,這樣就會報錯,因為壓縮文件當中的數據還在緩沖區,代表壓縮文件當中沒有數據,你去讀取,就只能讀取一個255,就達不到解壓縮的目的。怎么解決呢??? 刷新緩沖區

    解壓縮完整代碼:

    //解壓縮 void LZ77::UNCompressFile(const std::string& strFilePath) {//打開壓縮文件和標記文件FILE* fInD = fopen(strFilePath.c_str(), "rb");if (nullptr == fInD) {std::cout << "壓縮文件打開失敗" << std::endl;return;}//操作標記數據的文件指針FILE* fInF = fopen(strFilePath.c_str(), "rb");if (nullptr == fInF) {std::cout << "壓縮文件打開失敗" << std::endl;return;}//獲取源文件大小ULL fileSize = 0;fseek(fInF, 0 - sizeof(fileSize), SEEK_END);fread(&fileSize, sizeof(fileSize), 1, fInF);//獲取標記信息的大小size_t flagSize = 0;fseek(fInF, 0 - sizeof(fileSize) - sizeof(flagSize), SEEK_END);fread(&flagSize, sizeof(flagSize), 1, fInF);//將讀取標記信息的文件指針移動到保存標記數據的起始位置fseek(fInF, 0 - sizeof(flagSize) - sizeof(fileSize) - flagSize, SEEK_END);//開始解壓縮//寫解壓縮數據//FILE* ffOut = fopen("5.txt","rb");//std::string filestr ;//std::cout<<filestr<<std::endl;//fread(&filestr,sizeof(filestr),1,ffOut);//std::string str = "4.";//str += filestr;//fclose(ffOut);FILE* fOut = fopen("4.txt", "wb");assert(fOut);FILE* fR = fopen("4.txt", "rb");assert(fR);UCH bitCount = 0;UCH chFlag = 0;ULL encodeCount = 0;//標記已經解壓縮的數據大小,方便判斷解壓縮的結束while (encodeCount < fileSize) {//讀取標記if (0 == bitCount) {//讀取一個字節chFlag=fgetc(fInF);bitCount = 8;}if (chFlag & 0x80)//當前字節的高位為1 {//距離長度對USH matchLen = fgetc(fInD) + 3;//長度USH matchDist = 0;//距離fread(&matchDist, sizeof(matchDist), 1, fInD);//清空緩沖區,非常重要fflush(fOut);//更新已經解碼的字節數大小encodeCount += matchLen;//fR:讀取前文匹配串中的內容UCH ch;fseek(fR, 0 - matchDist, SEEK_END);while (matchLen) {ch = fgetc(fR);fputc(ch, fOut);--matchLen;//在還原長度距離對時,一定要清空緩沖區,否則可能會還原出錯fflush(fOut);}}else {//原字符UCH ch = fgetc(fInD);fputc(ch, fOut);encodeCount += 1;}chFlag <<= 1;bitCount--;}fclose(fInD);fclose(fInF);fclose(fOut);fclose(fR); }

    到這里,簡單的文件解壓縮已經OK,接下來就針對大文件的壓縮和解壓縮進行補充

    四、大文件處理


  • 更新哈希表:當把右窗當中的數據搬移到左窗后,左窗當中的數據已經被覆蓋掉,右窗當中的數據已經在start - WSIZE 處,所以需要更新哈希表
  • //更新哈希表 void HashTable::Update() {for (USH i = 0; i < WSIZE; ++i) {//先更新head,把大于等于WSIXE的下標 減去 WSIZE 放到哈希表的左窗內//把head中小于WSIZE 的下標直接置為0,因為小于WSIZE的下標//在查找緩沖區中距離先行緩沖區的距離太遠了,進行匹配不劃算if (head_[i] >= WSIZE)head_[i] -= WSIZE;elsehead_[i] = 0;//更新prev,和head同理if (prev_[i] >= WSIZE)prev_[i] -= WSIZE;elseprev_[i] = 0;} }

    2.填充窗口

    • 注意源文件就是小文件的情況,那么在壓縮一部分后,源文件已經沒有數據,文件指針已經走到文件的末尾,如果再去向右窗填充數據,就會報錯,需要判斷

    //向緩沖區中填充數據 void LZ77::FillWindow(FILE* fIn, size_t& lookAhead, USH& start) {//start壓縮已經進行到右窗,先行緩沖區剩余數據不夠MIN_LOOKAHEADif (start >= WSIZE) //如果不進行if判斷,在進行小文件壓縮時還會繼續//從文件中讀取數據,但是文件已經走到末尾{//1.將右窗中的數據搬移到左窗memcpy(pWin_, pWin_ + WSIZE, WSIZE);//將右窗中的內容清0,防止影響后續數據memset(pWin_ + WSIZE, 0, WSIZE);//更新start,start本來是大于WSIZE,經過1步驟后,start應該在左窗,start -= WSIZEc;//2.更新哈希表ht_.Update();//3.向右窗中補充WSIZE個的待壓縮數據if (!feof(fIn))//判斷文件是否走到末尾lookAhead += fread(pWin_ + WSIZE, 1, WSIZE, fIn);} }

    3.注意查找范圍和壓縮范圍重疊的情況

    • 下圖這種情況當指針走到BD的時候,匹配字符串的長度距離是多少?(5,3)

    • 為什么指針在E4的時候不進行匹配呢?按理說是可以進行一次匹配的。答案就是哈希表的結構引起的,我們在初始化的時候把哈希表當中的每個位置都設為0,代表匹配結束即到達匹配鏈的末尾,在這里,E4 BD A0經過哈希函數計算出哈希地址后,在相應哈希地址的位置放E4這個字符在緩沖區當中的下標,而這個下標剛好又為0,在第二個E4到來之后,拿到的匹配頭為0,所以是不進行匹配的

    • 這種情況下,還原長度距離對還需要刷新緩沖區,到這里解壓縮就需要2次刷新緩沖區





    到這里基于LZ77算法的文件壓縮已經完成

    五、總結

    我們的項目是基于GZIP的思想進行壓縮的。

    1.總結GZIP壓縮的思想

    2.項目的缺陷

    • 當前已完成基于Huffman和LZ77算法的文件壓縮與解壓縮
    • 改進的方向就是把Huffman和LZ77算法的文件壓縮與解壓縮結合起來,模擬GZIP
    • 但是不能直接把LZ77的壓縮結果用Huffman進行壓縮,原因是壓縮率不理想


    3.項目的改進方向

    • 不要直接對LZ77的壓縮結果采用Huffman樹的方法進行壓縮-----Huffman樹的變形:范式Huffman樹

    4.范式Huffman樹
    請看下節

    總結

    以上是生活随笔為你收集整理的基于LZ77算法的文件解压缩项目缺陷分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。