文件压缩
来源:互联网 发布:android图案解锁源码 编辑:程序博客网 时间:2024/06/18 04:01
文件压缩原理:
1. 建立最小堆
2. 利用最小堆的数据特性建立huffman树结构
3. 统计要压缩的文件中字符出现次数
4. 以字符出现次数为树的权重建立哈夫曼树
5. 遍历huffman树获取字符编码和行数(真正要存储的数据)
6. 创建一个文件(test.compress)存储压缩数据(行数、配置信息)
7. 将字符编码(用字符在huffman树从根节点到叶子结点的路径编码)以位图方式存储在test.compress文件中
文件解压原理:
1. 根据压缩文件test.compress内的配置信息重建huffman树
2. 根据行数跳过配置信息,读取压缩后的数据本体
3. 根据压缩位图中的二进制信息遍历重建的huffman树
4. 建立解压后的文件test.uncompress文件存储遍历得到的字符
源码:
heap.h
#pragma once#include <iostream>using namespace std;#include <vector>#include <assert.h>template<class T>struct Less{ bool operator()(const T& left,const T& right) { return left<right; }};template<class T>struct Great{ bool operator()(const T& left,const T& right) { return left>right; }};template<class T,class Compare=Less>class Heap{public: Heap() :_heap(NULL) {} Heap(const T* array,size_t size) :_heap(NULL) { _heap.resize(size); for(size_t i=0; i<size; i++) { _heap[i]=array[i]; } for (int root=(size-2)/2; root>0; root--) { BuildDown(root); } } void Push(const T& data) { _heap.push_back(data); for (int root=(_heap.size()-1)/2; root>=0; root--) { BuildDown(root); } } void Pop() { assert(!_heap.empty()); size_t size=_heap.size()-1; swap(_heap[size],_heap[0]); _heap.pop_back(); for (int root=_heap.size(); root>=0; root--) { BuildDown(root); } } bool Empty() { return _heap.empty(); } size_t Size() { return _heap.size(); } T Top() { return _heap[0]; }private: void BuildDown(size_t root) { size_t parent=root;//倒数第一个非叶子节点 size_t size=_heap.size(); size_t child=root*2+1; while (child<size) { if ((child+1<size)&&Compare()(_heap[child+1],_heap[child]))//右孩子存在情况下于左孩子比较找出最小值 { child=child+1; } if (Compare()(_heap[child],_heap[parent]))//若小于父母节点则交换 { swap(_heap[child],_heap[parent]); parent=child; child=parent*2+1; } else return; } }private: vector<T> _heap;};void test(){ int arr[]={12,18,24,3,32,74,5}; size_t size=sizeof(arr)/sizeof(arr[0]); Heap<int,Less<int>> h(arr,sizeof(arr)/sizeof(arr[0])); h.Push(1); h.Pop();}
HuffmanTree.hpp
#include "heap.hpp"template<class T>struct HuffmanNode{ HuffmanNode(const T& weight) :_Lchild(NULL) ,_Rchild(NULL) ,_weight(weight) {} //HuffmanNode* _Parent; HuffmanNode* _Lchild; HuffmanNode* _Rchild; T _weight;};template<class T>class Tree{ typedef HuffmanNode<T> Node;public: Tree() {} Tree(T* array,size_t size,const T& invalid) { CreatHuffmanTree(array,size,invalid); //invalid防止只有一个节点的情况 } ~Tree() { Destory(_pRoot); } Node* Root() { return _pRoot; }private: void CreatHuffmanTree(T* array,size_t size,const T& invalid) { struct Compare//重载比较器 { bool operator()(const Node* left,const Node* right) { return left->_weight<right->_weight; } }; Heap<Node*,Compare> h; for(size_t i=0; i<size; i++) { if (array[i] != invalid) { Node* pRoot=new Node(array[i]); h.Push(pRoot); } } if (h.Size()) { while (h.Size()>1) { Node *Left=h.Top(); h.Pop(); Node *Right=h.Top(); h.Pop(); Node* Parent=new Node(Left->_weight+Right->_weight); Parent->_Lchild=Left; Parent->_Rchild=Right; h.Push(Parent); } _pRoot=h.Top(); } else return ; //int tmp=(int)_pRoot->_weight._ch; //int tmp1=(int)_pRoot->_Lchild->_weight._ch; //int tmp2=(int)_pRoot->_Rchild->_weight._ch; } void Destory(Node* _pRoot) { if (_pRoot) { Destory(_pRoot->_Lchild); Destory(_pRoot->_Rchild); delete _pRoot; _pRoot=NULL; } }private: Node* _pRoot;};
file_compress.h
#define _CRT_SECURE_NO_WARNINGS#include "HuffmanTree.hpp"#include <string>#include <windows.h>#include <cstdlib>struct File_Info{ unsigned char _ch;//字符 long long _count;//出现次数 string _code;//字符编码 File_Info(const long long& count=0) :_count(count) {} File_Info(const char ch) :_ch(ch) {} File_Info operator+(const File_Info& f)const { return _count+f._count; } bool operator<(const File_Info& f)const { return _count<f._count; } bool operator!=(const File_Info& f)const { return _count!=f._count; }};class FileCompress{ typedef HuffmanNode<File_Info> Node;public: void Compress(const char* filename)//文件压缩 { FILE* pfr=fopen(filename,"r"); if (pfr==NULL) { perror("fail to open the file"); } //统计文件中字符出现次数 for (int i=0; i<256; i++) { _Info[i]._ch = i;//所有字符种类 } int ch = fgetc(pfr); while (ch!=EOF) { _Info[ch]._count++; ch=fgetc(pfr); } fclose(pfr);//重置文件指针 FILE* fpr=fopen(filename,"r"); Tree<File_Info> tree(_Info, 256, File_Info());//以File_Info结构体为树的权重建立哈夫曼树 Node* root=tree.Root(); string code; int line=0; _GetTreeNode(root,code,line);//获取字符编码和行号 //添加文件后缀,并重置文件指针 fseek(pfr,0,SEEK_SET); string write(filename); write=write+".compress";//存放压缩后数据 FILE* pfw=fopen(write.c_str(),"w"); fputs(write.c_str(),pfw);//写入文件名 fputc('\n',pfw); char Line[4];//存储行数 _itoa(line,Line,10);//转化行数为十进制字符放入文件 fputs(Line,pfw); fputc('\n',pfw); for(int i=0; i<256; i++)//写入文件配置信息 { if(_Info[i]._count)//排除没有出现的字符 { fputc(_Info[i]._ch,pfw); char arr[126]; _itoa(_Info[i]._count,arr,10);//转化_count为十进制字符放入文件 fputc(' ',pfw); fputs(arr,pfw);// fputc(':',pfw);// fputs(_Info[i]._code.c_str(),pfw); fputc('\n',pfw); } } unsigned char data = 0; int pos=7; ch=fgetc(pfr); while (ch!=EOF)//写入编码信息 { const char*ptr=_Info[ch]._code.c_str(); while (*ptr) { if (pos>=0) { data=data | ((*ptr-'0')<<pos); pos--; ptr++; } else if(pos<0) { fputc(data,pfw);//读满8位写入 pos=7; data=0; } } ch=fgetc(pfr);//读取下一个字符 } //最后一个字符不管有没有写满都要放进去 fputc(data,pfw); //fputc(EOF,pfw);//写入文件结束符 fputs("\r\n",pfw); fclose(pfr); fclose(pfw); cout<<"压缩完成"<<endl; } //文件解压 void Extract(const char* filename) { assert(filename); FILE* pfr=fopen(filename,"rb"); File_Info _Info[256]; string code; string write(filename); _GetLine(write.c_str(),_Info,code);//重写树节点 int index=write.rfind(".",write.size()); if (write.substr(index+1,write.size())!="Compress")//检查文件后缀是否正确 { return ; } write=write.substr(0, index);//除去文件后缀 write=write+".UnCompress"; FILE* pfw=fopen(write.c_str(),"wb");//添加新的后缀并打开解压文件 Tree<File_Info> newtree(_Info,256,File_Info()); HuffmanNode<File_Info> *root=newtree.Root(); HuffmanNode<File_Info> *cur=root; if (root==NULL) { return ; } long long charcount=root->_weight._count;//字符总数,控制循环条件 int pos=8; const char* ch=code.c_str();//压缩后的数据 while (charcount)//解压 { --pos; unsigned char v=1; int tmp=((*ch)&(v<<pos)); if (tmp) { cur=cur->_Rchild; } else if (tmp==0) { cur=cur->_Lchild; } if (cur->_Lchild==NULL && cur->_Rchild==NULL) { fputc(cur->_weight._ch,pfw);//找到编码对应叶节点的字符信息_ch cur=root; if (--charcount==0)//总字符数 { break; } } if (pos==0) { pos=8; ch++; } } fclose(pfr); fclose(pfw); cout<<"解压结束"<<endl; }protected: void _GetTreeNode(Node* root, string code,int& line)//获取字符编码和行数 { if (root==NULL) return; _GetTreeNode(root->_Lchild, code+'0',line); _GetTreeNode(root->_Rchild, code+'1',line); if (root->_Lchild==NULL && root->_Rchild==NULL) { _Info[root->_weight._ch]._code=code.c_str(); if (root->_weight._count!=0) { line++;//行数为叶节点个数 } } } void _GetLine(string filename,File_Info* _Info,string& code) { FILE* pfr=fopen(filename.c_str(),"r"); unsigned char ch=fgetc(pfr); while(ch!='\n')//最后一位'\n'已被读取 { ch=fgetc(pfr); } string Line;//存储行数 for (int i=0; i<4;i++)//行数最位4多 { ch=fgetc(pfr);//读行数 if (ch=='\n') break; Line.push_back(ch); } int line=atoi(Line.c_str()); //将行数转化为整型 while (line--) { string buf; //读取每一行信息 char ch=fgetc(pfr); while (ch!='\n') { buf.push_back(ch); ch=fgetc(pfr); } //建立叶节点信息 int index=buf.rfind(" ",buf.size()); //找到space对应下标 unsigned char c=buf[0]; //第一个字符为_ch _Info[c]._ch=c; string str=buf.substr(index,buf.size());//space之后为出现次数_count int count=atoi(str.c_str()); _Info[c]._count=count; } unsigned char h=fgetc(pfr);//读取压缩后的数据本体 while (h!=EOF) { code.push_back(h); h=fgetc(pfr); } //char *rdbuf=new char[1024]; //int rdsize=fread(rdbuf,1,1024,pfr); //while (rdsize!=0) //{ // break; // code+=fread(rdbuf,1,1024,pfr); //} //const unsigned char *cur=(const unsigned char *)code.c_str(); fclose(pfr); }private: File_Info _Info[256];};
阅读全文
1 0
- 文件压缩和解压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- 文件压缩
- Leetcode:493. Reverse Pairs
- Python里matplotlib图像里的中文输出问题
- JFreeChart实现图形报表
- C++基础---string类和标准模板库
- Spring学习笔记:自动创建Proxy
- 文件压缩
- Redis系列-8.键管理
- java基础-个人知识点杂记2.数据库基于日志的恢复
- pandas数据操作
- 技术篇~第一章 数据同步
- C语言之指针专题二:指针做函数参数是指针存在的最大意义
- 线程初步(五)---join,yield
- Java面试题全集(下)
- 写给自己·工作的第5年