数据结构与算法 课设 哈夫曼编译码 文件操作

首先需要三个txt文件
在这里插入图片描述
huffman.txt文件存入各个节点的值,data.txt存入要编码的字符串,code.txt存放编码的结果
数据结构与算法 课设 哈夫曼编译码 文件操作_第1张图片
如果出问题就是huffman.txt文件的最后一行不是空行,仔细看图中的光标位置
数据结构与算法 课设 哈夫曼编译码 文件操作_第2张图片
code.txt为空

//zstu-wcj
#include
#include
#include
using namespace std;
#define max 1000
typedef struct huffmantree//哈夫曼树结点
{
	char data;//结点的值
	int weight;//结点权值
	int parent, lchild, rchild;//父节点,左右结点在数组中的位置下标
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;
string chhh;//记录data.txt中的字符串
void Select(HuffmanTree& HT, int m, int& s1, int& s2)//在HT[k]中选择两个其双亲域为0且权值最小的结点,(1<=k<=i-1)
{                                                     // 并返回它们在HT中的序号s1和s2
	int i, min1 = 10000, min2 = 10000; //先赋予最大值
	for (i = 1; i <= m; i++)//找到未建树的最小值的下标
	{
		if (HT[i].weight < min1 && HT[i].parent == 0)  //当且仅当权值为最小值,且未构建到树中
		{
			min1 = HT[i].weight;
			s1 = i;//记录下标
		}
	}

	for (i = 1; i <= m; i++)//找到未建树的第二小的下标
	{
		if (HT[i].weight < min2 && HT[i].parent == 0 && i != s1)
		{
			min2 = HT[i].weight;
			s2 = i;//记录下标
		}
	}
}
void Read_Data_To_Tree(HuffmanTree& HT, int n)
{
	int m;
	m = 2 * n - 1;//m = 所有结点的个数
	for (int i = 0; i <= m; ++i) //将1~m号单元中的双亲、左孩子,右孩子的下标都初始化为0
	{
		HT[i].parent = 0;
		HT[i].lchild = 0;
		HT[i].rchild = 0;
	}
	ifstream myfile("huffman.txt");
	HT[1].data = ' ';//找不到将空格从文件读入的方法
	HT[1].weight = 186;//就先自己传了
	int len;
	for (len = 2; len <= n; ++len)//将huffman.txt的内容读到HT[]中
	{
		myfile >> HT[len].data >> HT[len].weight;
	}

	int s1, s2;
	for (int i = n + 1; i <= m; ++i)
	{
		Select(HT, i - 1, s1, s2);

		HT[s1].parent = i;
		HT[s2].parent = i;
		//得到新结点i,将s1和s2的父结点下标由0改为i
		HT[i].lchild = s1;
		HT[i].rchild = s2;                            //s1,s2分别作为i的左右孩子
		HT[i].weight = HT[s1].weight + HT[s2].weight; //i 的权值为左右孩子权值之和
	}
}

void Creat_HuffmanCode(HuffmanTree& HT, HuffmanCode* HC, int n)
{
	int c, f, start;
	*HC = new char* [n + 1];//*HC指向n个字符数组的第一个数组
	char* cd = new char[n];//暂时存放哈夫曼编码
	cd[n - 1] = '\0';
	for (int i = 1; i <= n; i++)
	{
		start = n - 1;//猜测每个编码的长度不会超过所有叶子结点的个数之和
		c = i;//先存储当前结点的下标
		f = HT[i].parent;//父结点的下标
		while (f != 0)//当HT[i]有父结点时,自底向上,寻找父结点,直到根结点为止
		{
			if (HT[f].lchild == c)//当父结点的左孩子==当前结点的下标时
				cd[--start] = '0';//左0
			else                             //因为是自底向上,所以是从cd的后往前填充编码
				cd[--start] = '1';//右1
			c = f;//c存储父结点的下标
			f = HT[f].parent;//f为父结点的父结点的下标
		}
		(*HC)[i] = new char[n - start];//(*HC)[i]指向第i个字符串
		strcpy_s((*HC)[i], n - start, &cd[start]);//将cd数组从start到末尾的部分复制到(*HC)[i]中
		//该处为避免STL检查使用了strcpy
		//但是第二个参数的设置没有仔细地验证
		//也许存在差1问题
	}
	delete[] cd;
}
void data_to_code(HuffmanTree& HT, HuffmanCode* HC, int n)//将正文编码写入code.txt
{
	string ch1, ch2;
	bool flag = true;//使ch2第一次不加空格
	ifstream infile("data.txt");
	while (infile >> ch1)//每次都只能读到空格
	{
		if (flag == false)
		{
			ch2 += " ";
		}
		ch2 += ch1;
		flag = false;
	}
	chhh = ch2;//data.txt中的完整字符串
	infile.close();//将data.txt的内容存入字符串ch中
	int num = ch2.size();

	//清空文件内容
	string dbfile = "code.txt";
	ofstream fileout(dbfile.c_str(), ios::out | ios::trunc);
	fileout.close();

	for (int i = 0; i < num; i++)//遍历ch2字符串
	{
		for (int j = 1; j <= n; j++)//从 HT[0].data到HT[n].data匹配字符
		{
			if (ch2[i] == HT[j].data || ch2[i] - 32 == HT[j].data)//当字符和HT[j].data相等 或 字符的ascii码-32转化为大写字母后与HT[j].data相等时
			{
				fstream f;
				f.open("code.txt", ios::out | ios::app);//文件追加输入方式
				f << (*HC)[j];//将HT[j].data 对应的哈夫曼编码输入到文件中
				f.close();
			}
		}
	}
	cout << "是否要打印各个字符对应的编码(1打印,2不打印):" << endl;
	int f = 100;
	cin >> f;
	if (f == 1)
	{
		for (int i = 1; i <= n; i++)
		{
			cout << HT[i].data << " " << (*HC)[i] << endl;
		}
	}
}

void decodingHuffmanCode(HuffmanTree HT, HuffmanCode* HC, bool* up, int n) {
	int p = 2 * n - 1;//HT的最后一个节点是根节点,前n个节点是叶子节点
	int i = 0;//指示测试串中的第i个字符
	int j = 0;//指示结果串中的第j个字符
	char result[100];//存储解码以后的字符串
	string coding;//存储code.txt中的哈夫曼编码

	ifstream infile("code.txt");
	infile >> coding;//将code.txt中的二进制字符串输入到coding
	infile.close();

	int num = coding.size();//二进制编码长度
	while (i < num)//依次读取二进制编码
	{
		if (coding[i] == '0')  p = HT[p].lchild;//若读到的编码为1,则p跳转到左孩子结点
		if (coding[i] == '1')  p = HT[p].rchild;//若读到的编码为0,则p跳转到左孩子结点

		if (p <= n)           //p<=n则表明p为叶子节点,因为在构造哈夫曼树HT时,HT的m个节点中前n个节点为叶子节点
		{
			result[j] = HT[p].data;
			j++;
			p = 2 * n - 1;//p重新指向根节点
		}
		i++;
	}
	result[j] = '\0';//结果串的结束符
	//因为 HT[p].data中的数据都为大写字母
	//所以result数组中全为大写字母
	//下面的for循环通过up数组的判定进行转换
	for (int c = 0; c <= j; c++)
	{
		if (up[c] == 1)//非小写字母时直接输出
		{
			cout << result[c];
		}
		else//小写字母时将result的ascii码+32转化为小写字母
		{
			char down = result[c] + 32;

			cout << down;
		}
	}
}
int main()
{
	HuffmanCode HC;
	HuffmanTree HT;

	fstream fin("huffman.txt", ios::in);
	char c;
	int line = 0;
	while (fin.get(c))//统计data.txt文件的行数,行数line+1即为结点数
	{
		if (c == '\n')//读到换行符时累加
			line++;
	}
	fin.close();
	int lenth = 0;
	int n = line + 1;//行数
	int m = 2 * n - 1;//结点数
	HT = new HTNode[m + 1];  //不用HTNode[0],所以分配m+1个单元,HT[m] 表示根结点

	cout << "************哈夫曼编译码***********" << endl;
	cout << "*********1.读取数据并建树**********" << endl;
	cout << "*********2.编码          **********" << endl;
	cout << "*********3.解码并打印    **********" << endl;
	cout << "*********4.打印各个编码  **********" << endl;
	cout << "***********************************" << endl;
	int choice = -1;
	while (choice != 0)
	{
		cout << "请输入:";
		cin >> choice;
		switch (choice)
		{
		case 1:
			Read_Data_To_Tree(HT, n);//将huffman.txt的数据读入HT中
			Creat_HuffmanCode(HT, &HC, n);//根据HT中的权值建树
			cout << "建立成功" << endl;
			break;
		case 2:
			data_to_code(HT, &HC, n);//将正文编码写入code.txt
			cout << "编码成功" << endl;
			break;
		case 3:
			bool up[100];//字符串中每个字符对应的flag,用来判断是否为大写字母
			lenth = chhh.size();//chhh是从data.txt中读到的字符串

			for (int i = 0; i < lenth; i++)//
			{
				up[i] = true;                   //默认每个字符都是大写
				if (chhh[i] > 90) up[i] = false;//当字符的ascii码值>90时设为false   (Z的ascii码为90)
			}
			cout << "解码为:" << endl;
			decodingHuffmanCode(HT, &HC, up, n);//解码
			cout << endl;
			break;
		}
	}

	return 0;
}

有不明白的地方可以加群讨论(没错只有我一个人)484266833

你可能感兴趣的:(哈夫曼,数据结构作业,数据结构)