[项目]文件压缩

文件的压缩和解压缩

由于存储一个int 最少需要8个字节 一个char 最少需要1个字节(32位),但大多数时候在一个文件中会有很多重复的数字或者字符,将其进行重新编码,比如说:s:1001 在这个文件中这就代表s 从而就产生了哈夫曼编码
哈夫曼编码: 最佳判定树

定义哈夫曼树之前先说明几个与哈夫曼树有关的概念:

 

路径: 树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。

 

路径长度:路径上的分枝数目称作路径长度。

 

树的路径长度:从树根到每一个结点的路径长度之和。

 

结点的带权路径长度:在一棵树中,如果其结点上附带有一个权值,通常把该结点的路径长度与该结点上的权值

                                        之积称为该结点的带权路径长度

树的带权路径长度:如果树中每个叶子上都带有一个权值,则把树中所有叶子的带权路径长度之和称为树的带

                                   权路径长度。

一般来说,用n(n>0)个带权值的叶子来构造二叉树,限定二叉树中除了这n个叶子外只能出现度为2的结点。
 
那么符合这样条件的二叉树往往可构造出许多颗,
 
 
其中带权路径长度最小的二叉树就称为哈夫曼树最优二叉树
 
特点:
根据哈弗曼树的定义,一棵二叉树要使其WPL值最小,必须使权值越大的叶子结点越靠近根结点,而权值越小的叶子结点
越远离根结点。
将权小的数据放在叶子节点,每次通过选取出最小的权构造出一个新节点再放入可被选择的区域,最终达到最佳的路线
所以:
(1)利用字符集中每个字符的使用频率作为权值构造一个哈夫曼树;
(2)从根结点开始,为到每个叶子结点路径上的左分支赋予0,右分支赋予1,并从根到叶子方向形成该叶子结点的编码

压缩前:
i am a student
压缩后:
a:2
d:1
e:1
i:1
m:1
n:1
s:1
t:2
u:1

然而压缩完以后失去了可读性
故还需将其恢复:解压缩
再将压缩文件恢复成原文件
压缩文件中存储哈夫曼编码(八位一存),解压缩文件通过读哈夫曼编码还原出字符
实现思路:
1.统计文件中字符出现的次数,利用数据结构堆建造Huffman树,出现次数多的编码短,出现次数少的编码长。
根据建造好的Huffman树形成编码,以对文件进行压缩。
2.将文件中出现的字符以及他们出现的次数写入配置文件,以便后续的解压缩。

3.根据配置文件读取相关信息重建Huffman树,对压缩后的文件进行译码。

#pragma once
#pragma once
#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
using namespace std;

template
       
       
         struct Less { bool operator() (const T & l, const T & r) { return l < r; } }; template 
        
          struct Greater { bool operator() (const T & l, const T & r) { return l > r; } }; template 
         
           > class Heap { private: vector 
          
            _array; protected: void Adjustdown(int root) { size_t child = root * 2 + 1; while (child < _array.size()) { if (child + 1 < _array.size() && Compare()(_array[child + 1], _array[child])) { child++; } if (Compare()(_array[child], _array[root])) { swap(_array[child], _array[root]); root = child; child = root * 2 + 1; } else { break; } } } void Adjustup(int child) { int root = (child - 1) / 2; while (root >= 0) { if (Compare() (_array[child], _array[root])) { swap(_array[child], _array[root]); child = root; root = (child - 1) / 2; } else { break; } } } public: Heap() {} Heap(T *array, int size) { int i; for (i = 0; i < size; i++) { _array.push_back(array[i]); } for (i = (_array.size() - 2) / 2; i >= 0; i--) { Adjustdown(i); } } void push(T data) { _array.push_back(data); Adjustup(_array.size() - 1); } void pop() { swap(_array[0], _array[_array.size() - 1]); _array.pop_back(); Adjustdown(0); } T & top() { assert(_array.size() > 0); return _array[0]; } size_t size() { return _array.size(); } }; 
           
          
         
       
      
      
     
     
    
    
   
   

#pragma once
#pragma once
#include
  
  
   
   
#include"heap.hpp"
using namespace std;

template
   
   
    
    
struct HuffmanTreeNode {
	HuffmanTreeNode
    
    
     
      *_left;
	HuffmanTreeNode
     
     
      
       *_right;
	HuffmanTreeNode
      
      
        *_parent; T _weight; //weight代表自定义类型的对象实体 HuffmanTreeNode(const T weight) :_left(NULL) , _right(NULL) , _weight(weight) , _parent(NULL) {} }; template 
       
         struct NodeCompare { bool operator()(HuffmanTreeNode 
        
          *l, HuffmanTreeNode 
         
           *r) { return l->_weight < r->_weight; } }; zemplate 
          
            class HuffmanTree { typedef HuffmanTreeNode 
           
             Node; public: HuffmanTree() :_root(NULL) {} void Create(const T *a, size_t size, T invalid) { assert(a); Heap 
            
              > minHeap; for (size_t i = 0; i < size; i++) { if (a[i] != invalid) { Node *newnode = new Node(a[i]); minHeap.push(newnode); } } while (minHeap.size() >= 2) { Node *left = minHeap.top(); minHeap.pop(); Node *right = minHeap.top(); minHeap.pop(); Node *parent = new Node(left->_weight + right->_weight); parent->_left = left; parent->_right = right; left->_parent = parent; right->_parent = parent; minHeap.push(parent); } _root = minHeap.top(); minHeap.pop(); } Node *GetRoot() { return _root; } private: Node *_root; };#pragma once #include"HuffmanTree.hpp" #include 
             
               #include 
              
                /* a 4次 b 3次 …… 共有 256 个字符,开辟256数组 */ typedef long LongType; //标识每个字符 struct FileInfo { char _ch; LongType _count; string _code; FileInfo(unsigned char ch = 0) :_ch(ch) , _count(0) {} bool operator < (const FileInfo &info) { return this->_count < info._count; } FileInfo operator + (const FileInfo &info) { FileInfo tmp; tmp._count = this->_count + info._count; return tmp; } bool operator != (const FileInfo &info)const { return this->_count != info._count; } }; class FileCompress { public: FileCompress() { for (int i = 0; i < 256; ++i) _infos[i]._ch = i; } public: bool Compress(const char* filename) { //1.打开文件,统计每个字符出现次数 assert(filename); FILE* fOut = fopen(filename,"rb"); assert(fOut); long long chSize = 0; char ch = fgetc(fOut); while (ch != EOF) { ++chSize; _infos[(unsigned char)ch]._count++; ch = fgetc(fOut); } //2.生成对应的Huffman编码 HuffmanTree 
               
                 > tree; FileInfo invalid; //指定非法值,_count为0 tree.Create(_infos, 256, invalid); string Ins; Ins.clear(); _GenerateHuffmanCode(tree.GetRoot()); //3.写入压缩文件 string compressfile = filename; compressfile += ".huffman"; FILE *fInCompress = fopen(compressfile.c_str(), "wb"); assert(fInCompress); fseek(fOut, 0, SEEK_SET); ch = fgetc(fOut); int index = 0; unsigned char Inch = 0; while (ch != EOF) { string &code = _infos[(unsigned char)ch]._code; for (size_t i = 0; i < code.size(); ++i) { Inch <<= 1; if (code[i] == '1') { Inch |= 1; } if (++index == 8) { fputc(Inch, fInCompress); index = 0; Inch = 0; } } ch = fgetc(fOut); } //处理最后一个不满1个Byte的单元 if (index != 0) { Inch <<= (8 - index); fputc(Inch, fInCompress); } //4.写配置文件 string configfile = filename; configfile += ".cfig"; FILE *fInConfig = fopen(configfile.c_str(), "wb"); assert(fInConfig); string str; //配置文件的第一行:> 文本总的chSize _itoa(chSize, (char*)str.c_str(), 10); fputs(str.c_str(), fInConfig); fputc('\n', fInConfig); for (size_t i = 0; i < 256; ++i) { string Inconfig; if (_infos[i]._count > 0) { Inconfig += _infos[i]._ch; Inconfig += ','; Inconfig += _itoa(_infos[i]._count,(char*)str.c_str(), 10); Inconfig += '\n'; } fputs(Inconfig.c_str(), fInConfig); str.clear(); } fclose(fOut); fclose(fInCompress); fclose(fInConfig); return true; } bool UnCompress(const char *filename) { //1.读取配置文件信息 string configfile = filename; configfile += ".cfig"; FILE *fOutConfig = fopen(configfile.c_str(), "rb"); assert(fOutConfig); string line; long long chSize = 0; //配置文件的第一行:> 文本总的chSize ReadLine(fOutConfig, line); chSize += atoi(line.c_str()); line.clear(); while (ReadLine(fOutConfig,line)) { if (!line.empty()) { unsigned char ch = line[0]; _infos[ch]._count = atoi(line.substr(2).c_str()); line.clear(); } else { line = '\n'; } } //2.构造哈夫曼树 HuffmanTree 
                
                  > tree; FileInfo invalid; //指定非法值,_count为0 tree.Create(_infos, 256, invalid); _GenerateHuffmanCode(tree.GetRoot()); //3.解压文件 string compressfile = filename; //读压缩文件 compressfile += ".huffman"; FILE *fOutCompress = fopen(compressfile.c_str(), "rb"); assert(fOutCompress); string uncompressfile = filename; //写解压缩文件 uncompressfile += ".uncompress"; FILE *fInUncompress = fopen(uncompressfile.c_str(), "wb"); assert(fInUncompress); char ch = fgetc(fOutCompress); HuffmanTreeNode 
                 
                   * cur = tree.GetRoot(); int pos = 8; while (1) { //叶子节点 if (cur->_left == NULL && cur->_right == NULL) { fputc(cur->_weight._ch,fInUncompress); cur = tree.GetRoot(); if(--chSize==0) break; } --pos; if (ch & (1 << pos)) //pos的最高位1则 右孩子 1< 
                  
                    _right; else cur = cur->_left; if (pos == 0) { ch = fgetc(fOutCompress); pos = 8; } } fclose(fOutConfig); fclose(fOutCompress); fclose(fInUncompress); return true; } protected: void _GenerateHuffmanCode(HuffmanTreeNode 
                   
                     * root) { if (root == NULL) return; _GenerateHuffmanCode(root->_left); _GenerateHuffmanCode(root->_right); if (root->_left == NULL && root->_right == NULL) { HuffmanTreeNode 
                    
                      * cur = root; HuffmanTreeNode 
                     
                       * parent = cur->_parent; // *cur类型为huffman类型的node 其中的weight成员代表元素类型info // info._ch 代表相应的字符char string& code = _infos[(unsigned char)cur->_weight._ch]._code; while (parent) { if (parent->_left == cur) code += '0'; else code += '1'; cur = parent; parent = cur->_parent; } reverse(code.begin(),code.end()); } } bool ReadLine(FILE *fOut, string &str) { char ch = fgetc(fOut); if (ch == EOF) return false; while (ch != EOF && ch != '\n') { str += ch; ch = fgetc(fOut); } return false; } private: FileInfo _infos[256]; }; 
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
         
        
      
     
     
    
    
   
   
  
  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值