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
程序图: