C++实现霍夫曼编码

Huffman类声明与实现:

Huffman.h

#ifndef HUFFMAN_H
#define HUFFMAN_H
#include
#include
#include
struct Huffman_Node;
class Huffman;
struct Huffman_Node
{
	Huffman_Node() = default;
	Huffman_Node(int w, char W, Huffman_Node *D, Huffman_Node *L,Huffman_Node *R,bool use = false)
	{
		weight = w;
		Word = W;
		Parent = D;
		Left = L;
		Right = R;
		Used = use;
	};
	~Huffman_Node() { };

	int weight;//权值,字符,父节点指针,左右孩子指针。
	char Word;
	Huffman_Node *Parent;
	Huffman_Node *Left;
	Huffman_Node *Right;
	bool Used;
 
};
class Huffman
{
public:
	Huffman() = default;
	Huffman(std::ifstream &In);//构造霍夫曼树。
	~Huffman() { };

	void Codeing();//编码。
	void Translating();//解码。
	void PrintCode();//在显示器上打印编码并格式化的写入文件。
	void PrintTree();//打印构建好的霍夫曼树。

private:
	std::vector Huf;//储存霍夫曼树节点。
	Huffman_Node *root;
	void print(Huffman_Node* p, std::string s,std::ofstream &Write);//打印霍夫曼树。
};
#endif // !HUFFMAN_H


Huffman.c


#include"Huffman.h"
#include
#include
#include
#include
#include
#include
#include
#include
bool Less(int &a, int &b);//用于对vector排序的可调用函数。
bool Less(int &a, int &b)//小于。
{
	return (a < b) ? true : false;
}
Huffman::Huffman(std::ifstream &In)//构造函数,初始化数据并构建霍夫曼树。
{
	if (!In.is_open())
	{
		std::cerr << "无法打开文件!" << std::endl;
		return;
	}

	while (!In.eof())
	{
		std::string Line;
		std::getline(In, Line);//从文件中取得一行。
		if (Line.empty())
			continue;
		std::string Word;//文件中的字符。
		std::string Weight;//文件中对应该字符的权值。


		std::istringstream Input(Line);//string流,分离数据。
		Input >> Word;//先提取字符,再提取权值。
		Input >> Weight;


		auto T_n = [](std::string s)->int {return atoi(s.c_str()); };//Lambad,得到int型权值。
		auto T_c = [](std::string ss)->char {return ss[0]; };//Lambad,从string转化为char。
		Huffman_Node *Node = new Huffman_Node(T_n(Weight), T_c(Word), NULL, NULL, NULL);

		Huf.push_back(Node);//储存。
	}


	In.close();//文件读取结束,关闭!
			   //构建霍夫曼树。
	std::vector WeightOfHuf;//获取霍夫曼树节点的权值。
	for (auto X : Huf)
		WeightOfHuf.push_back(X->weight);

	std::sort(WeightOfHuf.begin(), WeightOfHuf.end(), Less);//调用标准库函数,对权值容器内容排序。

	while (WeightOfHuf.size() != 1)
	{
		Huffman_Node *Deal_1 = NULL, *Deal_2 = NULL;//用来处理各个节点。
		for (auto Find_1 : Huf)//找到第一个最小节点。
			if (Find_1->weight == WeightOfHuf[0] && Find_1->Used == false)
			{
				Find_1->Used = true;//改为已被使用过。
				Deal_1 = &(*Find_1);
				break;
			}
		for (auto Find_2 : Huf)
			if (Find_2->weight == WeightOfHuf[1] && Find_2->Used == false)//找到第二小的节点。
			{
				Find_2->Used = true;
				Deal_2 = &(*Find_2);
				break;
			}
		//构建新节点,权值为两项之和,字符为#,并连接左右孩子节点。
		Huffman_Node *NewNode = new Huffman_Node(Deal_1->weight + Deal_2->weight, '#', NULL, &(*Deal_1), &(*Deal_2));
		Deal_1->Parent = &(*NewNode);//将孩子节点的父节点指针连起来。
		Deal_2->Parent = &(*NewNode);

		//新节点权值储存在权值容器中并添加如节点容器。
		WeightOfHuf.push_back(NewNode->weight);
		Huf.push_back(NewNode);
		//删除已经处理过的两项权值。
		auto It = WeightOfHuf.end() - 1;//将指针定位在容器末尾。

		WeightOfHuf[0] = *It;
		WeightOfHuf[1] = *(It - 1);
		WeightOfHuf.pop_back();//删除两个元素。
		WeightOfHuf.pop_back();

		std::sort(WeightOfHuf.begin(), WeightOfHuf.end(), Less);//重排权值容器

	}
	//root指针指向霍夫曼树根节点。
	for (auto X : Huf)
		if (X->weight == WeightOfHuf[0])
		{
			root = &((*X));
			break;
		}

}

void Huffman::Codeing()//编码。
{
	std::cout << "输入想要编码的文件地址!" << std::endl;
	std::string ReadCode_Path;
	std::getline(std::cin, ReadCode_Path);//获取编码文件地址。

	std::ifstream In_Code(ReadCode_Path);//定义一个读文件流,读取信息。
	if (!In_Code.is_open())//判断文件是否打开正常。
	{
		std::cerr << "无法代开文件,文件地址错误!" << std::endl;
		return;
	}

	std::cout << "输入编码数据储存文件地址!" << std::endl;
	std::string WriteCode_Path;
	std::getline(std::cin, WriteCode_Path);//获取编码储存文件地址。

	while (!In_Code.eof())
	{
		std::string Line;//一行原数据。
		std::string Trans_Line;//编码结果。
		std::getline(In_Code, Line);//读取一行处理。
		if (Line.empty())
			continue;
		for (int i = 0; i < Line.length(); ++i)//对一行中的每一个字符进行编码。
		{
			Huffman_Node *Current_Node = NULL;
			if (Line[i] == ' ')//排除空格。
				continue;
			else
			{
				for (auto X : Huf)//找到该字符对应的霍夫曼节点。
					if (X->Word == Line[i])
					{
						Current_Node = &(*X);
						break;
					}
			}
			std::stack Code;//栈用来储存编码。
			Code.push(" ");//先追加一个空格,方便格式化输出。

						   //从找到的节点开始回溯至根节点,得到一个字符的编码。
			while (Current_Node != root)
			{
				if (Current_Node->Parent != NULL && Current_Node == Current_Node->Parent->Left)//是父节点的左孩子节点。
				{
					Code.push("0");//左节点应为编码0.
					Current_Node = Current_Node->Parent;//指针移向父节点。
				}
				if (Current_Node->Parent != NULL && Current_Node == Current_Node->Parent->Right)//是父节点的右孩子节点。
				{
					Code.push("1");//右节点应为编码1.
					Current_Node = Current_Node->Parent;//指针移向父节点。
				}
			}
			while (!Code.empty())//得到真正的编码。
			{
				Trans_Line.append(Code.top());
				Code.pop();
			}
		}
		//一行处理完毕,写入目标文件。
		std::ofstream Out_Code(WriteCode_Path, std::ofstream::out | std::ofstream::app);//以代开文件每次定位在文件尾的形式创建写文件流。
		Out_Code << Trans_Line << std::endl;//写入一行。
	}
}

std::ofstream& operator<<(std::ofstream& Out, std::vector Vec)//重载运算符<<,使后边译码内容得以方便写入文件。
{
	for (auto X : Vec)
		Out << X;
	return Out;
}

void Huffman::Translating()//译码。
{
	std::cout << "输入需要译码的文件地址!" << std::endl;
	std::string Trans_Path;
	std::getline(std::cin, Trans_Path);

	std::ifstream ReadTrans(Trans_Path);//读取译码文件内容。

	std::cout << "输入译码后结果保存文件地址!" << std::endl;
	std::string TransSave_Path;
	std::getline(std::cin, TransSave_Path);

	std::ofstream WriteTrans(TransSave_Path, std::ofstream::out | std::ofstream::app);//写文件流。

	while (!ReadTrans.eof())
	{
		std::string Line;
		std::getline(ReadTrans, Line);
		if (Line.empty())
			continue;
		std::istringstream DealLines(Line);//string流,处理每一段编码。
		std::string Deal;

		std::vector Trans_Inf;//储存一行译码结果。
		while (DealLines >> Deal)//得到一段编码并处理。
		{
			Huffman_Node *Current_Node = root;//从根节点开始。
			for (int i = 0; i < Deal.length(); ++i)//根据编码找到字符。
			{
				switch (Deal[i])
				{
				case '1'://编码为1,右孩子节点。
				{
					Current_Node = Current_Node->Right;//更改指针。
					break;
				}
				case '0'://编码为0,左孩子节点。
				{
					Current_Node = Current_Node->Left;
					break;
				}
				default:
					break;
				}
			}
			Trans_Inf.push_back(Current_Node->Word);//得到编码对应的字符。

		}
		WriteTrans << Trans_Inf << std::endl;//向文件写入译完的一行。
	}
}

void Huffman::PrintCode()//打印在终端上。
{
	//将codefile文件以紧凑格式显示在终端上。
	std::cout << "输要在终端上显示内容的文件的地址!" << std::endl;
	std::string Print_Path;
	std::getline(std::cin, Print_Path);

	std::ifstream ReadPrint(Print_Path);//读文件内容。

	std::string Show;
	while (!ReadPrint.eof())//先将文件内容全部拷贝。
	{
		std::string word;
		std::getline(ReadPrint, word);
		std::string SubstrOfShow;
		std::istringstream DealStr(word);//绑定string流。
		while (DealStr >> SubstrOfShow)
			Show.append(SubstrOfShow);
	}
	for (int i = 0; i < Show.length(); ++i)//每50个输出。
	{
		if (i % 50 == 0 && i != 0)//50个打一行。
			std::cout << std::endl;
		else
			std::cout << Show[i];
	}
	std::cout << std::endl;
	//将次形式的内容写在文件codeprint中。

	std::cout << "输入想以在终端上显示的内容为样式储存数据的文件地址!" << std::endl;

	std::string Print;
	std::getline(std::cin, Print);

	std::ofstream WritePrint(Print, std::ofstream::out | std::ofstream::app);//写文件。
	for (int i = 0; i < Show.length(); ++i)
	{
		if (i % 50 == 0)//每50个编码为一行。
			WritePrint << std::endl;
		else
			WritePrint << Show[i];
	}
}
void Huffman::print(Huffman_Node *p, std::string s, std::ofstream &Write)
{
	if (p == NULL)
		return;
	s += "      ";
	print(p->Right, s, Write);//先打右子树。
	std::cout << s << p->Word << std::endl;
	Write << s << p->Word << std::endl;//后打左子树。
	print(p->Left, s, Write);

}
void Huffman::PrintTree()//以凹入表的形式打印构建好的霍夫曼树。
{
	std::string s = "";

	std::cout << "输入霍夫曼树凹入表储存的文件地址!" << std::endl;
	std::string Tree_File;
	std::getline(std::cin, Tree_File);

	std::ofstream Write_Tree(Tree_File, std::ofstream::out | std::ofstream::app);//写文件流。
	if (!Write_Tree.is_open())
	{
		std::cerr << "文件路径错误!" << std::endl;
		return;
	}

	print(root, s, Write_Tree);

}


源文件:


#include"Huffman.h"
#include
#include
#include
#include
using namespace std;
ofstream &operator<<(ofstream &Write, map M);//重载map的<<操作符,方便写入文件。
int main()
{
	//从键盘输入字符和权值,储存在文件中。
	//从文件读取内容,初始化霍夫曼树。
	cout << "**********                  霍夫曼编码/解码器                **********" << endl;
	cout << "**********--1.输入数字 1 直接从终端输入要解码的字符的权值!  **********" << endl;
	cout << "**********--2.输入数字 2 从给定的文章中统计字符权值,再编码!**********" << endl;
	cout << "**********--3 输入数字 3 从存放权值信息的文件编码!          **********" << endl;
	while (true)
	{
		int chose;
		cin >> chose;
		if (!cin.fail())
		{
			if(chose == 1 || chose == 2 || chose == 3)
				switch (chose)
			{
			case 1:
			{
				int N;
				cout << "输入字符集N的大小!" << endl;
				cin >> N;
				vector VEc;
				for (int i = 0; i <= N; ++i)//收集字符权值。
				{
					string Get;
					if (i == 0)
					{
						getline(cin, Get);
						continue;
					}
					cout << "输入一个字符及其权值" << endl;
					getline(cin, Get);
					VEc.push_back(Get);
				}

				cout << "输入权值内容要存放的文件的地址!" << endl;
				string InfPath;
			
				getline(cin, InfPath);
				ofstream In(InfPath, ostream::out | ostream::app);
				for (auto S : VEc)
				{
					In << S << endl;
				}
				ifstream Get(InfPath);//获取权值文件。
									  //实例化对象!
				Huffman HufTree(Get);

				HufTree.Codeing();//编码。
			    HufTree.Translating();//解码。
				HufTree.PrintCode();//打印编码。

				HufTree.PrintTree();//打印构建的树。
				break;
			}
			case 2:
			{
				cout << "输入要编码的文件地址!" << endl;
				string text_Path;
				string Else;
				getline(cin, Else);
				getline(cin, text_Path);//输入编码文件地址。

				ifstream Read_Inf(text_Path);//读取文件内容。

				map Weight_Cal;//统计权值匹配容器。
				while (!Read_Inf.eof())
				{
					string line;
					getline(Read_Inf, line);

					istringstream Do(line);
					for (int i = 0; i < line.length(); ++i)
						if (Weight_Cal.find(line[i]) == Weight_Cal.end())//没找到。添加。
							Weight_Cal[line[i]] = 1;
						else
							Weight_Cal.find(line[i])->second += 1;//找到,数目加一。
				}
				cout << "输入储存权值文件的地址!" << endl;
				string path;
				getline(cin, path);

				ofstream Write_Weight(path, ofstream::out | ofstream::app);
				if (!Write_Weight.is_open())
				{
					cout << "文件地址错误!" << endl;
					return -1;
				}
				Write_Weight << Weight_Cal;//权值统计数据写入文件。

				ifstream Get(path);//获取权值文件。
								   //实例化对象!
				Huffman HufTree(Get);

				HufTree.Codeing();//编码。
				HufTree.Translating();//解码。
				HufTree.PrintCode();//打印编码。

				HufTree.PrintTree();//打印构建的树。
				break;
			}
			case 3:
			{
				string path;
				cout << "输入权值文件地址!" << endl;
				string Else;
				getline(cin, Else);
				getline(cin, path);
				ifstream Get(path);//获取权值文件。
								   //实例化对象!
				Huffman HufTree(Get);

				HufTree.Codeing();//编码。


				HufTree.Translating();//解码。
				HufTree.PrintCode();//打印编码。

				HufTree.PrintTree();//打印构建的树。
				break;
			}
			default:
				break;
			}
			else
			{
				cout << "请做出正确选择!" << endl;
				continue;
			}
			break;
		}
		else
		{
			cin.sync();
			cin.clear();
			cout << "    请做出正确选择!" << endl;
		}
	}
	system("pause");
}
ofstream &operator<<(ofstream &Write, map M)//重载map的<<操作符,方便写入文件。
{
	for (auto It = M.begin(); It != M.end(); ++It)
		Write << It->first << " " << It->second << endl;
	return Write;
}


程序图:



你可能感兴趣的:(C++,算法,数据结构,C++,数据结构与算法)