数据结构C语言---文件的加密和解密

本篇的主要目的是利用所学的数据结构的知识对一个任意文件进行加密和解密。

在文件加密过程中,常用的数据结构包括哈希表、树结构(如二叉搜索树、哈夫曼树)、堆、链表等。选择合适的数据结构取决于加密算法的需求和特性。

选择合适的加密算法和数据结构对保障数据安全至关重要。常见的加密标准如AES、RSA等提供了高度的安全性保障。不同的数据结构和算法会对加密和解密的性能产生影响,需要权衡安全性和性能需求。

一、部分代码的介绍

1.这段代码是一个实现哈夫曼编码(Huffman coding)的程序框架,主要用于对文本文件进行压缩和解压缩操作。

#include <iostream> //C++ 流输入输出库
#include <fstream> //C++ 文件流输入输出库
#include <cstdio> //C 标准输入输出库
#include <cstdlib> //C 标准函数库
#include <cstring> //C 字符串函数库
#include <string> //C++ string类库
#include <map> //C++ map映射库
#include <queue> //C++ queue队列库
#include <algorithm> //C++ 常用算法库

using namespace std;

/********************** Huffman树结点 **********************/
//Huffman树单个结点
struct node {
	string s; //字符串存单个字符(含中文)
	int weight; //记录当前结点的权值
	node* Left, * Right; //左子树指针,右子树指针
};
//自定义结点优先级(用于优先队列)
class Compare_Huffman_node {
public:
	bool operator()(node*& a, node*& b)const {
		return b->weight < a->weight; //小顶堆
	}
};
typedef struct node* HuffmanTree, * HuffmanNode; //哈夫曼树指针,哈夫曼树结点指针

/********************** 全局变量 **********************/
map<string, int> FrequencyTable; //字符频率表
map<string, string> CodeTable; //哈夫曼编码表
priority_queue<HuffmanNode, vector<HuffmanNode>, Compare_Huffman_node> Q; //优先队列,取权值最小结点的堆
string Source_s, ZeroOne_s, Decoding_s; //存储 源文件, 哈夫曼编码文件,解码文件 的字符串
string CodeTable_s; //存储 译码表 的字符串
const int C_Bit = 8; //C_Bit位压缩

1.1.结构体 node:

用于构建哈夫曼树的节点结构体。
string s: 存储单个字符(可以包含中文字符)。
int weight: 记录当前节点的权值。
node* Left, * Right: 左子树和右子树的指针。


1.2.自定义比较类 Compare_Huffman_node:

用于优先队列 priority_queue 中节点的比较。
operator() 函数重载,实现小顶堆的效果,即权值小的节点优先级高。


1.3.全局变量:

FrequencyTable: 字符频率表,用 map<string, int> 存储字符及其出现频率。
CodeTable: 哈夫曼编码表,用 map<string, string> 存储字符及其对应的哈夫曼编码。
priority_queue<HuffmanNode, vector<HuffmanNode>, Compare_Huffman_node> Q: 优先队列,存储哈夫曼树节点指针,根据权值从小到大排序。
Source_s, ZeroOne_s, Decoding_s: 分别存储源文件、哈夫曼编码后的文件、解码后的文件的字符串内容。
CodeTable_s: 存储译码表的字符串。


1.4.常量 C_Bit:

定义为常量 8,可能用于表示每个字符的位数,通常与字节对齐有关。
这段代码的目的是使用哈夫曼编码对输入的文本文件进行压缩和解压缩操作。它涉及构建哈夫曼树、生成编码表、对文本进行编码和解码等操作。


二、加密代码

2.这段代码是一个用于文件操作的函数集合,主要用于保存加密文件、加载加密文件和获取文件大小。

void Out_File(string op, void(*write)(ofstream&)) {
	ofstream outfile; //文件输出流
	cout << "\t请输入存放" << op << "文件的路径及文件名: " << endl;
	cout << "示例:" << "C:\\Users\\lgc2621690255\\source\\repos\\Project1\\Project1\\" << op << "文件.txt" << endl;
	string file_name; cin >> file_name;
	outfile.open(file_name); //打开指定文件
	if (!outfile.is_open()) { //如果打开文件失败
		cout << "文件 " << file_name << " 打开失败!" << endl;
		cout << "请检查: 1.文件是否已存在 2. 路径是否正确 3.是否无权限访问该文件 / 文件夹" << endl;
		exit(-1); //终止程序,抛出异常值
	}
	write(outfile);
	outfile.close(); //关闭文件流 
	cout << endl; //纯属美观,无实际意义
}
//保存加密文件
void Save_Encrypted() {
	fstream file; //文件输出流
	cout << "\t请输入存放" << "加密" << "文件的路径及文件名: " << endl;
	cout << "示例:" << "C:\\Users\\lgc2621690255\\source\\repos\\Project1\\Project1\\" << "加密" << "文件.Huffman" << endl;
	string file_name; cin >> file_name;
	file.open(file_name, ios::binary | ios::out); //打开指定文件
	if (!file) { //如果打开文件失败
		cout << "文件 " << file_name << " 打开失败!" << endl;
		cout << "请检查: 1.文件是否已存在 2. 路径是否正确 3.是否无权限访问该文件 / 文件夹" << endl;
		exit(-1); //终止程序,抛出异常值
	}
	Write_Encrypted(file);
	file.close(); file.clear();//关闭文件流 
}
//加载加密文件
void Load_Encrypted() {
	fstream file; //文件输入流
	cout << "\t请输入" << "加密" << "文件的存放路径及文件名: " << endl;
	cout << "示例:" << "C:\\Users\\lgc2621690255\\source\\repos\\Project1\\Project1\\" << "加密" << "文件.Huffman" << endl;
	string file_name; cin >> file_name;
	file.open(file_name, ifstream::in | ios::binary); //打开指定文件
	if (!file) { //如果打开文件失败
		cout << "文件 " << file_name << " 打开失败!" << endl;
		cout << "请检查: 1.文件是否存在 2. 路径是否正确 3.是否无权限访问该文件 / 文件夹" << endl;
		exit(-1); //终止程序,抛出异常值
	}
	Read_Encrypted(file);
	file.close(); file.clear();//关闭文件流
}
//获取文件大小
size_t Get_FileSize(const string& file_name) {
	ifstream in(file_name.c_str());
	in.seekg(0, ios::end);
	size_t size = in.tellg();
	in.close();
	return size; //单位是:byte
}

2.1函数解释
2.1.1.void Out_File(string op, void(*write)(ofstream&))
功能: 用于将数据写入指定路径的文件。
参数:
op: 表示操作类型(如加密、解码)的字符串,用于提示用户输入文件路径时的上下文信息。
write: 函数指针,指向一个接受 ofstream 引用参数的写入操作函数,用于实际写入文件操作。

2.1.2.void Save_Encrypted()
功能: 保存加密后的数据到文件。
实现:
提示用户输入加密文件的存放路径及文件名。
打开指定路径的文件,以二进制写入模式。
如果文件打开失败,输出错误信息并终止程序。
调用 Write_Encrypted 函数,将加密后的数据写入文件。
关闭文件流并清除流状态。


2.1.3.void Load_Encrypted()
功能: 加载加密后的文件数据。
实现:
提示用户输入加密文件的存放路径及文件名。
打开指定路径的文件,以二进制读取模式。
如果文件打开失败,输出错误信息并终止程序。
调用 Read_Encrypted 函数,读取加密文件的内容。
关闭文件流并清除流状态。


2.1.4.size_t Get_FileSize(const string& file_name)
功能: 获取指定文件的大小(单位:字节)。
参数:
file_name: 要获取大小的文件的路径和文件名。
返回值: 文件的大小,以字节为单位。
实现:
打开指定路径的文件,以获取文件大小。
使用 seekg 定位到文件末尾,然后调用 tellg 获取当前位置,即文件大小。
关闭文件流并返回文件大小。


三、加密和解密

3.这段代码是一个用于文件操作的函数集合,主要用于保存加密文件、加载加密文件和获取文件大小。

void Coding(HuffmanTree T, string s) {
	if (T->Left == NULL && T->Right == NULL) { //当走到了叶子结点
		CodeTable[T->s] = s; //存储该结点内字符的01编码
		return;
	}
	Coding(T->Left, s = s + "0"); //左边走,编码 + 0
	s.pop_back(); //回溯时 编码长度 - 1
	Coding(T->Right, s = s + "1"); //右边走 编码 + 1
	s.pop_back(); //回溯时 编码长度 - 1
}
//对原始文件加密
void Encryption() {
	int size = Source_s.size(); string s; //获取原始文件的长度
	for (int i = 0; i < size; ++i) {
		s = Source_s[i]; //逐个处理字符

		if (Source_s[i] > 127 || Source_s[i] < 0) { //处理非ASCII码的字符(中文字符)
			++i; s = s + Source_s[i];
		}
		ZeroOne_s = ZeroOne_s + CodeTable[s]; //将编码表中 字符对应的 01编码 存入加密字符串
	}
}
//对加密文件解码。传入参数:加密用的哈夫曼树
void Decryption(HuffmanNode T) {
	int i = 0, size = ZeroOne_s.size(); //获取加密文件的长度
	HuffmanNode _T = T; //存储哈夫曼树的根结点
	while (i < size) {
		int cnt = 0; T = _T; //重置变量
		while (T->Left != NULL && T->Right != NULL) { //当没走到叶子结点时
			if (ZeroOne_s[i + cnt] == '0') T = T->Left; //是 0 则向左子树走
			else if (ZeroOne_s[i + cnt] == '1') T = T->Right; //是 1 则向左子树走
			++cnt; //本次编码的长度 + 1
		}
		Decoding_s = Decoding_s + T->s; //结果存入解码字符串中
		i += cnt; //已走长度i 加上 本次编码的长度 cnt
	}
}

3.1.void Coding(HuffmanTree T, string s)
功能: 通过递归遍历哈夫曼树,为每个叶子节点(字符节点)生成哈夫曼编码,并存储在 CodeTable 中。
参数:
T: 当前遍历的哈夫曼树节点。
s: 当前节点路径上的编码字符串。
实现:
如果当前节点是叶子节点(即没有左右子节点),将其字符 T->s 和对应的编码 s 存入 CodeTable。
否则,递归处理左子树和右子树,分别在编码末尾加入 '0' 和 '1',表示左右移动。
在递归回溯时,通过 s.pop_back() 操作来减少编码字符串的长度,以继续处理其他可能的编码路径。
3.2.void Encryption()
功能: 将原始文件(包含非ASCII字符)转换为哈夫曼编码序列。
实现:
遍历原始文件 Source_s 中的每个字符。
对于非ASCII字符(例如中文),由于在编码时,中文字符占据两个字节,因此需要特殊处理。
将每个字符或字符对应的编码从 CodeTable 中获取,并将其拼接到 ZeroOne_s 中,形成加密后的编码序列。
3.3.void Decryption(HuffmanNode T)
功能: 根据给定的哈夫曼树对加密后的编码序列 ZeroOne_s 进行解码。
参数:
T: 加密时使用的哈夫曼树的根节点。
实现:
初始化索引 i 和哈夫曼树节点 T。
循环处理 ZeroOne_s 中的每个字符,根据字符 '0' 或 '1',沿着哈夫曼树向左或向右移动。
当遍历到叶子节点时,将叶子节点代表的字符 T->s 加入 Decoding_s 中,并将索引 i 向前移动到已处理的编码末尾。
这样,逐步解码整个加密序列,直到 ZeroOne_s 中的所有编码都被处理完毕。


四、完整代码

#include <iostream> //C++ 流输入输出库
#include <fstream> //C++ 文件流输入输出库
#include <cstdio> //C 标准输入输出库
#include <cstdlib> //C 标准函数库
#include <cstring> //C 字符串函数库
#include <string> //C++ string类库
#include <map> //C++ map映射库
#include <queue> //C++ queue队列库
#include <algorithm> //C++ 常用算法库

using namespace std;

/********************** Huffman树结点 **********************/
//Huffman树单个结点
struct node {
	string s; //字符串存单个字符(含中文)
	int weight; //记录当前结点的权值
	node* Left, * Right; //左子树指针,右子树指针
};
//自定义结点优先级(用于优先队列)
class Compare_Huffman_node {
public:
	bool operator()(node*& a, node*& b)const {
		return b->weight < a->weight; //小顶堆
	}
};
typedef struct node* HuffmanTree, * HuffmanNode; //哈夫曼树指针,哈夫曼树结点指针

/********************** 全局变量 **********************/
map<string, int> FrequencyTable; //字符频率表
map<string, string> CodeTable; //哈夫曼编码表
priority_queue<HuffmanNode, vector<HuffmanNode>, Compare_Huffman_node> Q; //优先队列,取权值最小结点的堆
string Source_s, ZeroOne_s, Decoding_s; //存储 源文件, 哈夫曼编码文件,解码文件 的字符串
string CodeTable_s; //存储 译码表 的字符串
const int C_Bit = 8; //C_Bit位压缩

/********************** 菜单栏 **********************/
void Encryption_Menu(); //加密选项菜单
void Decryption_Menu(); //解码选项菜单
void Compare_Menu(); //比较文件菜单

/********************** 文件操作 **********************/
string Open_File(string, void(*p)(ifstream&)); //打开文件。 传入参数:操作名称,读入字符的函数指针
void Out_File(string, void(*p)(ofstream&)); //输出文件,传入参数:操作名称,写入字符的函数指针
void Save_Encrypted(); //保存加密文件
void Load_Encrypted(); //加载加密文件
size_t Get_FileSize(const string&); //获取文件大小

/********************** Huffman树 **********************/
HuffmanTree Build_Huffman(); //构造哈夫曼树
HuffmanTree Rebuild_Huffman(); //重构哈夫曼树

/********************** 读出写入子函数 **********************/
void Read_Source(ifstream&); //读入原始文件。传入参数:ifstream流
void Read_Decoding(ifstream&);//读入解码文件
void Read_Encrypted(fstream&); //读入加密文件。传入参数:ifstream流
void Read_CodeTable(ifstream&); //读入译码表文件。传入参数:ifstream流
void Write_ZeroOne(ofstream&); //输出出01文件
void Write_Decoding(ofstream&); //输出解码文件
void Write_CodeTable(ofstream&); //输出译码表文件
void Write_Encrypted(fstream&); //输出加密文件

/********************** 加密解码 **********************/
void Coding(HuffmanTree, string); //获取哈夫曼编码表。传入参数:构建好的哈夫曼树,用于存编码的字符串
void Encryption(); //对原始文件进行哈夫曼加密
void Decryption(HuffmanTree); //对加密文件解码。传入参数:哈夫曼树

//重置全局变量
void init() {
	FrequencyTable.clear(); CodeTable.clear();
	while (!Q.empty()) Q.pop();
	Source_s.clear(), ZeroOne_s.clear(), Decoding_s.clear();
	CodeTable_s.clear();
}
int main() {
	system("color 02"); //换个背景颜色
	while (true) {
		cout << "欢迎使用本系统进行文件加密压缩/解码还原!" << endl;
		cout << "请输入选项前的数字以继续操作:" << endl;
		cout << "\t1.文件加密" << endl;
		cout << "\t2.文件解码" << endl;
		cout << "\t3.文件比较" << endl;
		cout << "\t0.退出系统" << endl;
		int op; cin >> op;
		switch (op)
		{
			//文件加密
		case 1: system("cls"); Encryption_Menu();  break;
			//文件解码
		case 2: system("cls"); Decryption_Menu(); break;
			//文件比较
		case 3: system("cls"); Compare_Menu(); break;
			//退出系统
		case 0: cout << "退出系统成功!" << endl; return 0;
		default:
			cout << "输入选项有误,请检查后重新输入!" << endl;
			system("pause"); system("cls");
			break;
		}
	}
}

/********************** 菜单栏 **********************/
//加密选项菜单
void Encryption_Menu() {
	Open_File("原始", Read_Source); //打开原始文件
	cout << "打开原始文件成功,正在构造Huffman树..." << endl;
	HuffmanTree T = Build_Huffman(); //根据频率表构造哈夫曼树
	cout << "Huffman树构造成功,正在进行Huffman编码..." << endl;
	Coding(T, ""); //根据哈夫曼树获取哈夫曼编码
	cout << "编码成功,正在加密..." << endl;
	Encryption(); //对原始文件进行哈夫曼加密
	cout << "加密成功!" << endl;
	cout << "********************************************************************\n" << endl;
	Save_Encrypted(); //压缩并保存加密文件
	cout << "加密文件保存完成!\n" << endl;
	//Out_File("01", Write_ZeroOne); //输出01文件
	Out_File("译码表", Write_CodeTable); //输出译码表文件
	cout << "操作完成!请牢记您输入的加密文件地址以及译码表地址!" << endl;
	system("pause"); system("cls");
	init(); //重置全局变量
}
//解码选项菜单
void Decryption_Menu() {
	Load_Encrypted(); //加载并解压缩加密文件
	cout << "加密文件解压缩成功!\n" << endl;
	Open_File("译码表", Read_CodeTable);
	cout << "译码表加载完毕,正在重构Huffman树..." << endl;
	HuffmanTree T = Rebuild_Huffman(); //根据译码表重构哈夫曼树
	cout << "Huffman树重构完毕,正在对加密文件进行解码..." << endl;
	Decryption(T); //对加密文件进行解码
	cout << "解码成功!" << endl;
	cout << "********************************************************************\n" << endl;
	Out_File("解码", Write_Decoding); //输出解码文件
	cout << "操作完成!请牢记您输入的解码文件地址!" << endl;
	system("pause"); system("cls");
	init(); //重置全局变量
}
//比较文件菜单
void Compare_Menu() {
	Source_s.clear(); string file_name = Open_File("原始", Read_Source);
	size_t Source_size = Get_FileSize(file_name);
	cout << "\n------------------------------------------- 原始文件 -------------------------------------------\n" << endl;
	cout << Source_s;
	cout << "\n------------------------------------------- 原始文件 -------------------------------------------\n" << endl;
	Decoding_s.clear(); file_name = Open_File("解码", Read_Decoding);
	size_t Decoding_size = Get_FileSize(file_name);
	cout << "\n------------------------------------------- 解码文件 -------------------------------------------\n" << endl;
	cout << Decoding_s;
	cout << "\n------------------------------------------- 解码文件 -------------------------------------------\n" << endl;

	cout << "*********************************************************************************" << endl;
	cout << "\t原始文件大小:" << Source_size << " 字节" << endl;
	cout << "\t解码文件大小:" << Decoding_size << " 字节" << endl;
	if (Source_s == Decoding_s) cout << "\t经比对,原始文件与译码文件一致!" << endl;
	else cout << "\t经比对,原始文件与译码文件不一致!" << endl;
	cout << "*********************************************************************************\n" << endl;

	cout << "\t请输入存放" << "加密" << "文件的路径及文件名: " << endl;
	cout << "示例:" << "C:\\Users\\lgc2621690255\\source\\repos\\Project1\\Project1\\" << "加密" << "文件.Huffman" << endl;
	file_name; cin >> file_name; size_t Encryption_size = Get_FileSize(file_name);
	cout << "*********************************************************************************" << endl;
	cout << "\t原始文件大小:" << Source_size << " 字节" << endl;
	cout << "\t加密文件大小:" << Encryption_size << " 字节" << endl;
	cout << "\t压缩比为:" << (1.0 - (double)Encryption_size / (double)Source_size) * 100 << "%" << endl;
	cout << "*********************************************************************************\n" << endl;
	system("pause"); system("cls");
	init(); //重置全局变量
}
/********************** 菜单栏 **********************/


/********************** 文件操作 **********************/
//打开文件
string Open_File(string op, void(*read)(ifstream&)) {
	ifstream infile; //文件输入流
	cout << "\t请输入" << op << "文件的存放路径及文件名: " << endl;
	cout << "示例:" << "C:\\Users\\lgc2621690255\\source\\repos\\Project1\\Project1\\" << op << "文件.txt" << endl;
	string file_name; cin >> file_name;
	infile.open(file_name); //打开指定文件
	if (!infile.is_open()) { //如果打开文件失败
		cout << "文件 " << file_name << " 打开失败!" << endl;
		cout << "请检查: 1.文件是否存在 2. 路径是否正确 3.是否无权限访问该文件 / 文件夹" << endl;
		exit(-1); //终止程序,抛出异常值
	}
	read(infile); //从文件流读入字符
	infile.close(); //关闭文件流
	cout << endl; //纯属美观,无实际意义
	return file_name;
}
//输出文件,传入参数:加密/解码文件字符串,"加密"/"解码"
void Out_File(string op, void(*write)(ofstream&)) {
	ofstream outfile; //文件输出流
	cout << "\t请输入存放" << op << "文件的路径及文件名: " << endl;
	cout << "示例:" << "C:\\Users\\lgc2621690255\\source\\repos\\Project1\\Project1\\" << op << "文件.txt" << endl;
	string file_name; cin >> file_name;
	outfile.open(file_name); //打开指定文件
	if (!outfile.is_open()) { //如果打开文件失败
		cout << "文件 " << file_name << " 打开失败!" << endl;
		cout << "请检查: 1.文件是否已存在 2. 路径是否正确 3.是否无权限访问该文件 / 文件夹" << endl;
		exit(-1); //终止程序,抛出异常值
	}
	write(outfile);
	outfile.close(); //关闭文件流 
	cout << endl; //纯属美观,无实际意义
}
//保存加密文件
void Save_Encrypted() {
	fstream file; //文件输出流
	cout << "\t请输入存放" << "加密" << "文件的路径及文件名: " << endl;
	cout << "示例:" << "C:\\Users\\lgc2621690255\\source\\repos\\Project1\\Project1\\" << "加密" << "文件.Huffman" << endl;
	string file_name; cin >> file_name;
	file.open(file_name, ios::binary | ios::out); //打开指定文件
	if (!file) { //如果打开文件失败
		cout << "文件 " << file_name << " 打开失败!" << endl;
		cout << "请检查: 1.文件是否已存在 2. 路径是否正确 3.是否无权限访问该文件 / 文件夹" << endl;
		exit(-1); //终止程序,抛出异常值
	}
	Write_Encrypted(file);
	file.close(); file.clear();//关闭文件流 
}
//加载加密文件
void Load_Encrypted() {
	fstream file; //文件输入流
	cout << "\t请输入" << "加密" << "文件的存放路径及文件名: " << endl;
	cout << "示例:" << "C:\\Users\\lgc2621690255\\source\\repos\\Project1\\Project1\\" << "加密" << "文件.Huffman" << endl;
	string file_name; cin >> file_name;
	file.open(file_name, ifstream::in | ios::binary); //打开指定文件
	if (!file) { //如果打开文件失败
		cout << "文件 " << file_name << " 打开失败!" << endl;
		cout << "请检查: 1.文件是否存在 2. 路径是否正确 3.是否无权限访问该文件 / 文件夹" << endl;
		exit(-1); //终止程序,抛出异常值
	}
	Read_Encrypted(file);
	file.close(); file.clear();//关闭文件流
}
//获取文件大小
size_t Get_FileSize(const string& file_name) {
	ifstream in(file_name.c_str());
	in.seekg(0, ios::end);
	size_t size = in.tellg();
	in.close();
	return size; //单位是:byte
}

/********************** 文件操作 **********************/


/********************** Huffman树 **********************/
//构造哈夫曼树
HuffmanTree Build_Huffman() {
	HuffmanTree T; T = new node; //新建一颗哈夫曼树
	T->s = ""; T->weight = 0; T->Right = T->Left = NULL; //赋初值
	while (Q.size() > 1) { //当优先队列(森林)内元素数量 > 1
		T = new node; //新建一颗哈夫曼树
		T->Left = Q.top(), Q.pop(); //左子树为森林内权值最小的树
		T->Right = Q.top(), Q.pop(); //右子树为森林内权值次小的树
		T->weight = T->Left->weight + T->Right->weight; //当前结点的权值为左右子树权值之和
		Q.push(T); //将新构造的树重新插入优先队列中
	}
	T = Q.top(), Q.pop(); //森林内仅存的一颗哈夫曼树
	return T;
}
//重构Huffman树
HuffmanTree Rebuild_Huffman() {
	HuffmanTree T, _T; T = new node; //新建一颗哈夫曼树
	T->s = ""; T->weight = 0; T->Right = T->Left = NULL; //赋初值
	_T = T;
	for (auto it : CodeTable) {
		int size = it.first.size();
		T = _T;
		for (int i = 0; i < size; ++i) {
			HuffmanNode t; t = new node; //新建一颗哈夫曼树
			t->s = ""; t->weight = 0; t->Right = t->Left = NULL; //赋初值

			if (it.first[i] == '0') {
				if (T->Left == NULL) T->Left = t;
				T = T->Left;
			}
			else {
				if (T->Right == NULL) T->Right = t;
				T = T->Right;
			}

			if (i == size - 1) T->s = it.second;
		}
	}
	return _T;
}
/********************** Huffman树 **********************/


/********************** 读出写入子函数 **********************/
void Write_ZeroOne(ofstream& out) { out << ZeroOne_s; } //输出01文件
void Write_Decoding(ofstream& out) { out << Decoding_s; } //输出解码文件
 //输出译码表文件
void Write_CodeTable(ofstream& out) {
	CodeTable_s.clear(); for (auto it : CodeTable) CodeTable_s += it.first + " " + it.second + "\n";
	out << CodeTable_s;
}
//输出加密文件
void Write_Encrypted(fstream& file) {
	int size = ZeroOne_s.size();
	unsigned char last_bit = (unsigned char)(size % C_Bit + '0'); file.write((char*)&last_bit, sizeof(last_bit));
	for (int i = 0; i < size; i += C_Bit) {
		int res = 0;
		for (int j = 0; i + j < size && j < C_Bit; ++j) res = res * 2 + (ZeroOne_s[i + j] - '0');
		unsigned char ch = res;
		file.write((char*)&ch, sizeof(ch));
	}
}
//读入数据。传入参数:ifstream流
void Read_Source(ifstream& in) {
	char ch; string s;
	while (in.peek() != EOF) { //当文件流不到文件结尾时
		ch = in.get(); s = ch; //一个一个字符读入
		if (ch > 127 || ch < 0) { //处理非ASCII码的字符(中文字符)
			ch = in.get(); s = s + ch;
		}
		++FrequencyTable[s]; //字符 s 频率 + 1
		Source_s = Source_s + s; //原始文件字符串
	}
	//遍历字符频率表
	for (auto it : FrequencyTable) {
		HuffmanNode t = new node; //新建一个哈夫曼结点
		t->s = it.first; t->weight = it.second; t->Left = NULL; t->Right = NULL; //为其赋值
		Q.push(t); //加入到优先队列(堆)中
	}
}
//读入解码文件
void Read_Decoding(ifstream& in) {
	char ch; string s;
	while (in.peek() != EOF) { //当文件流不到文件结尾时
		ch = in.get(); s = ch; //一个一个字符读入
		if (ch > 127 || ch < 0) { //处理非ASCII码的字符(中文字符)
			ch = in.get(); s = s + ch;
		}
		Decoding_s = Decoding_s + s; //原始文件字符串
	}
}
//读入加密文件。传入参数:ifstream流
void Read_Encrypted(fstream& file) {
	unsigned char ch; int last_bit = -1;
	//依次读取二进制文件中的unsigned char值读入到变量i,并输出
	while (file.read((char*)&ch, sizeof(ch))) {
		if (last_bit == -1) { last_bit = ch - '0'; continue; }
		int t = ch;
		string s;
		for (int i = 0; i < C_Bit; ++i) {
			s = s + (char)((t & 1) + '0'); t >>= 1;
		}
		reverse(s.begin(), s.end());
		ZeroOne_s = ZeroOne_s + s;
	}
	if (last_bit != 0) {
		int size = ZeroOne_s.size();
		string t; for (int i = size - last_bit; i < size; ++i) t = t + ZeroOne_s[i];
		for (int i = 0; i < C_Bit; ++i) ZeroOne_s.pop_back();
		ZeroOne_s = ZeroOne_s + t;
	}
	//cout << ZeroOne_s << endl;
}
//读入译码表
void Read_CodeTable(ifstream& in) {
	char ch; string s;
	while (in.peek() != EOF) { //当文件流不到文件结尾时
		ch = in.get(); s = ch; //一个一个字符读入
		if (ch > 127 || ch < 0) { //处理非ASCII码的字符(中文字符)
			ch = in.get(); s = s + ch;
		}
		string coding; in >> coding; in.get();
		CodeTable[coding] = s;
	}
	//for (auto it : CodeTable) cout << it.first << " " << it.second << endl;
}
/********************** 读出写入子函数 **********************/


/********************** 加密解码 **********************/
//获取哈夫曼编码表。传入参数:构建好的哈夫曼树,用于存编码的字符串
void Coding(HuffmanTree T, string s) {
	if (T->Left == NULL && T->Right == NULL) { //当走到了叶子结点
		CodeTable[T->s] = s; //存储该结点内字符的01编码
		return;
	}
	Coding(T->Left, s = s + "0"); //左边走,编码 + 0
	s.pop_back(); //回溯时 编码长度 - 1
	Coding(T->Right, s = s + "1"); //右边走 编码 + 1
	s.pop_back(); //回溯时 编码长度 - 1
}
//对原始文件加密
void Encryption() {
	int size = Source_s.size(); string s; //获取原始文件的长度
	for (int i = 0; i < size; ++i) {
		s = Source_s[i]; //逐个处理字符

		if (Source_s[i] > 127 || Source_s[i] < 0) { //处理非ASCII码的字符(中文字符)
			++i; s = s + Source_s[i];
		}
		ZeroOne_s = ZeroOne_s + CodeTable[s]; //将编码表中 字符对应的 01编码 存入加密字符串
	}
}
//对加密文件解码。传入参数:加密用的哈夫曼树
void Decryption(HuffmanNode T) {
	int i = 0, size = ZeroOne_s.size(); //获取加密文件的长度
	HuffmanNode _T = T; //存储哈夫曼树的根结点
	while (i < size) {
		int cnt = 0; T = _T; //重置变量
		while (T->Left != NULL && T->Right != NULL) { //当没走到叶子结点时
			if (ZeroOne_s[i + cnt] == '0') T = T->Left; //是 0 则向左子树走
			else if (ZeroOne_s[i + cnt] == '1') T = T->Right; //是 1 则向左子树走
			++cnt; //本次编码的长度 + 1
		}
		Decoding_s = Decoding_s + T->s; //结果存入解码字符串中
		i += cnt; //已走长度i 加上 本次编码的长度 cnt
	}
}
/********************** 加密解码 **********************/

五、总结

本篇主要是构造哈夫曼树算法来构建一棵特定结构的二叉树来实现字符集的编码和解码。

哈夫曼树加密(编码)过程:

首先,需要统计待加密的文本中每个字符出现的频率。根据字符频率构建哈夫曼树。哈夫曼树是一棵满足最优前缀编码条件的二叉树,频率较高的字符位于树的较低层,频率较低的字符位于树的较高层。从根节点开始,向左走为添加 '0',向右走为添加 '1',直到到达叶子节点,记录从根节点到每个字符的路径所对应的编码。
使用生成的编码表,将原始文本中的每个字符替换为对应的哈夫曼编码序列。将所有字符替换为对应的哈夫曼编码序列后,得到加密后的文本数据,即哈夫曼编码串。


哈夫曼树解密(解码)过程:

使用相同的哈夫曼树结构,从根节点开始,根据加密后的哈夫曼编码逐位解码每个字符。
从根节点出发,根据每个 '0' 向左子树移动,根据每个 '1' 向右子树移动,直到达到叶子节点。
每次到达叶子节点时,将对应的字符添加到解码结果中,并从根节点重新开始解码下一个字符。按照哈夫曼编码串的长度,逐步解码整个加密文本,直到所有编码序列都被解析并转换为原始字符序列。

哈夫曼树的核心优势在于,对于频率分布不均匀的文本数据,可以有效地减少编码后的位数,从而实现高效的数据压缩和解压缩。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值