2019.12.19 updata 解压函数最后一行应返回真。
须知 课程设计作业,bug较多,懒得擦屁股
课程设计主要熟悉Huffman编码之类,瞎搞的 简单压缩 增大程序。
首先不能压缩文件夹,懒得搞了。。
运行程序输入1或2之后以 一个 换行或者空格 当作分隔符。
测试了几个视频,图片虽然无损但是效果不咋滴,还是某些字符占比高的文本文件效果比较好。
代码:
主函数
#include "huffmantree.h"
#include "File_operate.h"
#include <bits/stdc++.h>
using namespace std;
int main()
{
string filename, op;
while(1)
{
system("cls");
puts("输入1压缩\n输入2解压\n输入其他结束");
cin >> op;
getchar();
if(op == "1")
{
puts("拖入文件或者输入目标路径");
getline(cin, filename);
if(filename[0] == '"') filename = filename.substr(1, filename.size()-2);
File_operate *it;
it = new File_operate(filename);
if(it -> compression()) puts("压缩成功");
else puts("压缩失败");
delete(it);
system("pause");
}
else if(op == "2")
{
puts("拖入文件或者输入目标路径");
getline(cin, filename);
if(filename[0] == '"') filename = filename.substr(1, filename.size()-2);
File_operate *it;
it = new File_operate(filename);
if(it -> decompression()) puts("解压成功");
else puts("解压失败");
delete(it);
system("pause");
}
else break;
}
return 0;
}
huffmantree.h
#define ONCE
#ifndef HUFFMANTREE_H_INCLUDED
#define HUFFMANTREE_H_INCLUDED
#include <unordered_map>
#include <algorithm>
#include <queue>
#include <string>
using std::unordered_map;
using std::priority_queue;
using std::string;
struct Node
{
int weight, pos;
int fa, lson, rson;
Node() = default;
Node(int a, int b, int c, int d, int e):weight(a),pos(b),fa(c),lson(d),rson(e){}
bool operator < (const Node &_) const
{
return weight == _.weight ? pos > _.pos : weight > _.weight;
}
};
struct Denode
{
int id;
int lson, rson;
Denode()
{
id = -1;
lson = rson = 0;
}
};
class HuffmanTree
{
public:
void Creat(unordered_map<int, int> &);
string Translate(const int &);
// map[]运算会在索引项不存在的时候自动创建一个对象,所以函数不能加const,傻逼BUG
protected:
int n;
unordered_map<int, string> Hash;
priority_queue<Node> Queue;
Node *HT;
};
#endif // HUFFMANTREE_H_INCLUDED
huffmantree.cpp
#include "huffmantree.h"
string HuffmanTree::Translate(const int &wtf)
{
return Hash[wtf];
}
void HuffmanTree::Creat(unordered_map<int, int> &cnt)
{
n = cnt.size();
if(n <= 1) return;
int m = (n<<1)-1, index = 0;
HT = new Node[m];
for(int i = 0; i < m; ++i) HT[i] = Node(0,i,0,0,0);
for(auto tmp:cnt)
{
HT[index].weight = tmp.second;
Queue.push(HT[index++]);
}
Node x, y;
for(int i = n; i < m; ++i)
{
x = Queue.top(); Queue.pop();
y = Queue.top(); Queue.pop();
HT[x.pos].fa = i;
HT[y.pos].fa = i;
HT[i].lson = x.pos;
HT[i].rson = y.pos;
HT[i].weight = x.weight+y.weight;
Queue.push(HT[i]);
}
Hash.clear();
auto iter = cnt.begin();
for(int i = 0; i < n; ++i, ++iter)
{
string tmp = "";
for(int la = i, f = HT[i].fa; f; la = f, f = HT[f].fa)
{
if(HT[f].lson == la) tmp += '0';
else tmp += '1';
}
reverse(tmp.begin(),tmp.end());
Hash[iter->first] = tmp;
}
delete []HT;
}
File_operate.h
#define ONCE
#ifndef FILE_OPERATE_H_INCLUDED
#define FILE_OPERATE_H_INCLUDED
#include "huffmantree.h"
#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <ostream>
#include <fstream>
#include <string>
#include <string>
using std::iostream;
using std::unordered_map;
using std::ofstream;
using std::ifstream;
using std::string;
using std::ios;
using std::cout;
using std::endl;
using std::string;
class File_operate
{
public:
File_operate(string s):path(s){}
bool Readfile(string); // 压缩读取文件字符信息
void dataout(string, ofstream &); // 压缩输出文件信息
bool compression(); // 压缩
bool decompression(); // 解压
protected:
Denode *root;
string path;
unordered_map<int, int> cnt; // 字符出现次数
HuffmanTree *tree;
};
#endif // FILE_OPERATE_H_INCLUDED
File_operate.cpp
#include "File_operate.h"
// 压缩读取文件字符信息
bool File_operate::Readfile(string filename)
{
cnt.clear();
ifstream in_file(filename, ios::in|ios::binary);
if(!in_file.is_open()) return 0;
for(unsigned char ch; ch = in_file.get(), in_file; ++cnt[int(ch)]);
++cnt[256];
in_file.close();
return 1;
}
// 压缩字符
void File_operate::dataout(string filename, ofstream & out_file)
{
ifstream in_file(filename, ios::in|ios::binary);
unsigned char ch, outch;
string tmp;
char code[305];
int head = 0, tail = 0, mod = 300, len = 0, wtf;
while(in_file)
{
ch = in_file.get();
// 255 文本结束符,每次都进来了,导致每次解压都大了一点,找了半天bug
if(!in_file && ch == 255) break;
tmp = tree->Translate(int(ch));
for(int i = 0; i < tmp.size(); ++i)
{
code[tail] = tmp[i];
tail = (tail+1)%mod;
++len;
}
for(wtf = 0; wtf || len >= 8; --len)
{
if(!wtf) outch = 0;
outch <<= 1;
if(code[head] == '1') outch |= 1;
wtf = (wtf+1)%8;
head = (head+1)%mod;
if(!wtf) out_file << outch;
}
}
in_file.close();
tmp = tree->Translate(256);
for(int i = 0; i < tmp.size(); ++i)
{
code[tail] = tmp[i];
tail = (tail+1)%mod;
++len;
}
for(wtf = 0; len > 0 || wtf; --len)
{
if(!wtf) outch = 0;
outch <<= 1;
if(len > 0 && code[head] == '1') outch |= 1;
wtf = (wtf+1)%8;
head = (head+1)%mod;
if(!wtf) out_file << outch;
}
}
// 压缩 将原文件名,字符和编码,以及压缩后的字符,打印到输出文件里
bool File_operate::compression()
{
if(!Readfile(path)) return 0;
string name, newpath, shortname;
int flag = 0, check = 0, index = path.size()-1;
for(int i = path.size()-1; ~i; --i)
{
if(path[i] == '\\') flag = 1;
if(path[i] == '.' && !check) check = 1, index = i;
if(flag) newpath += path[i];
}
for(int i = index-1; ~i && path[i] != '\\'; --i) shortname += path[i];
for(int i = index; i < path.size(); ++i) name += path[i];
reverse(newpath.begin(), newpath.end());
reverse(shortname.begin(), shortname.end());
if(name == ".wzy") return 0;
tree = new HuffmanTree();
tree -> Creat(cnt);
ofstream out_file(newpath+shortname+".wzy",ios::out|ios::binary);
if(!out_file.is_open()) return 0;
out_file << name << "\\ ";
out_file << cnt.size() << " ";
for(auto tmp:cnt)
{
out_file << tmp.first << " " << (tree -> Translate(tmp.first)) << " ";
}
dataout(path, out_file);
delete tree;
out_file.close();
return 1;
}
/***压缩***/
/***解压***/
bool File_operate::decompression()
{
// 确保文件正确,找到输出格式
if(path.size() < 4) return 0;
string newpath, wzy;
wzy = path.substr(path.size()-4,4);
if(wzy != ".wzy") return 0;
newpath = path.substr(0,path.size()-4);
ifstream in_file(path, ios::in|ios::binary);
in_file >> wzy;
newpath += wzy.substr(0,wzy.size()-1);
ofstream out_file(newpath,ios::out|ios::binary);
// 建树
string code;
int n;
in_file >> n;
root = new Denode[n<<1];
for(int i = 0, index = 1, id; i < n; ++i)
{
in_file >> id >> code;
int now = 0;
for(int i = 0; i < code.size(); ++i)
{
if(code[i] == '0')
{
if(root[now].lson == 0)
root[now].lson = index++;
now = root[now].lson;
}
else
{
if(root[now].rson == 0)
root[now].rson = index++;
now = root[now].rson;
}
}
root[now].id = id;
}
int now = 0, flag = 0;
int ch = in_file.get(); // 文件指针应该还指向空格
// 输出原数据
while(in_file)
{
ch = in_file.get();
for(int i = 7; ~i; --i)
{
if((1<<i)&ch) now = root[now].rson;
else now = root[now].lson;
if(~root[now].id)
{
if(root[now].id == 256)
{
flag = 1;
break;
}
out_file << (unsigned char)(root[now].id);
now = 0;
}
}
if(flag) break;
}
delete []root;
in_file.close();
out_file.close();
}