C++小專案 — 基於huffman壓縮演算法的檔案壓縮專案
阿新 • • 發佈:2019-01-08
先去讀配置檔案,構建huffman樹和huffman編碼,用壓縮檔案裡的編碼去huffman樹中查詢,找到對應的葉子結點. 就把葉子結點的字元寫入到解壓縮
檔案中. 所以總結起來也就是那麼幾步:
1.讀取配置檔案,統計所有字元的個數.
2.構建huffman樹,讀解壓縮檔案,將所讀到的編碼字元的這個節點所含的字元,寫到解壓縮的檔案中,直到將壓縮檔案讀完
3.解壓縮完成之後,利用軟體測試,再然後測試一下壓縮率.
好啦話不多說 上程式碼 分析過程:
最後我們終於成功的,大概列出來程式碼的框架和主題思想,接下來就是我在寫程式碼當中碰到的一些BUG和錯誤,我將這些總結起來. (1).剛開始寫的時候測試發現如果待壓縮檔案中出現了中文,程式就會崩潰,將錯誤定位到構建哈夫曼編碼的函式處,最後發現是陣列越界的錯誤 ,因為如果只是字元,它的範圍是-128~127,程式中使用char型別為陣列下標(0~127),所以字元沒有問題. 但是漢字的編碼是兩個位元組,所以可能會 出現越界,解決方法就是將char型別強轉為unsigned char,可表示範圍為0~255. (2)檔案恢復的時候需要注意那些問題? 有些特殊字元在處理需要注意一下,比如'\n',我的程式中有一個函式就是讀取一行字元,但是若是該字元本身就是一個'\n'呢? 這就非常的棘手 了. 對於這個問題一定好好好處理,讀取配置檔案時若讀到了'\n',則說明該字元就是'\n',應該繼續讀取它的次數. (3)解壓縮檔案生成後丟失了大部分的內容 這個BUG困擾我很久,最後為了測試我讓程式打印出在壓縮的時候它寫入了多少次,然後再讓程式在解壓縮的時候列印自己讀出了多少次. 最後我發現 在解壓縮讀出的時候出現了問題,究其原因我又發現了很久,我發現它過早的退出了迴圈,那麼真相只有一個就是程式讀到了EOF,但是我在文字末尾 才會是EOF啊,因為解壓縮讀取是二進位制方式讀寫的,檔案這麼長! 他可能碰巧讀到了EOF對於的二進位制程式碼. 然後就退出啦. 解決方案: 使用feof()函式或者記錄字元資料個數然後根據個數迴圈,這兩種方法都可以解決上述問題. 這個專案其實就是利用我們學習過的堆和huffman樹的實際應用來解決我們實際生活的一些小問題,這種小專案還需要我們多多練習呢~ 利用程式碼解決生活的問題才是程式設計的意義,所以呢我們需要將理論和實際相結合~ 這樣我們的學習就會越來越好~//構建huffman編碼的函式. void GenerateHuffmanCode(Node* root) { if (root == NULL) { return; } if (root->_left == NULL &&root->_right == NULL) { string& code = _infos[(unsigned char)root->_w._ch]._code; Node* cur = root; Node* parent = cur->_parent; while (parent) { if (parent->_left == cur) { code.push_back('0'); } if (parent->_right == cur) { code.push_back('1'); } cur = parent; parent = cur->_parent; } reverse(code.begin(), code.end()); } GenerateHuffmanCode(root->_left); GenerateHuffmanCode(root->_right); } //解壓縮檔案 void Uncompress(const char* filename) { //為了區分解壓出來的檔案,不覆蓋原檔名. assert(filename); struct _huffmanInfo { char _ch; LongType _count; }; string uncompressFile = filename; //讓pos等於找到第一個'.'出現的的下標位置 size_t pos = uncompressFile.rfind('.'); assert(pos != string::npos); //從0開始取pos個字元 uncompressFile = uncompressFile.substr(0, pos); uncompressFile += ".unhuffman"; FILE* fIn = fopen(uncompressFile.c_str(), "wb"); size_t size; assert(fIn); FILE* fout = fopen(filename, "rb"); //往解壓縮檔案中維護的_infos表當中寫資料,然後利用這個_infos表生產huffman樹. while (1) { _huffmanInfo info; size = fread(&info, sizeof(_huffmanInfo), 1, fout); assert(1 == size); if (info._count) { _infos[(unsigned char)info._ch]._ch = info._ch; _infos[(unsigned char)info._ch]._count = info._count; } else break; } //重新構建huffman樹. CharInfo invalid; invalid._count = 0; HuffmanTree<CharInfo> tree(_infos, 256, invalid); //解壓縮 Node* root = tree.GetRoot(); GenerateHuffmanCode(tree.GetRoot()); //統計一共有多少個數據. LongType charcount = root->_w._count; char value; value = fgetc(fout); Node* cur = root; int count = 0; //往解壓縮檔案當中開始寫入資料. 在這裡直接針對性的先統計出所有元素的個數. //然後按照個數進行迴圈,寫完迴圈結束. while (charcount) { for (int pos = 7; pos >= 0; --pos) { if (value & (1 << pos)) //1 cur = cur->_right; else //0 cur = cur->_left; if (cur->_left == NULL && cur->_right == NULL) { fputc(cur->_w._ch, fIn); cur = root; --charcount; if (charcount == 0) { break; } } } value = fgetc(fout); } fclose(fIn); fclose(fout); }