问题描述:采用哈夫曼编码思想实现文本文件的压缩和恢复功能。
基本要求:
(1)菜单包括:
1.录入被压缩文件名
2.压缩文件
3.恢复文件
4.验证恢复的文件是否正确
5.退出
(2)对于压缩文件功能要求:压缩前显示被压缩文件内容,然后显示对各个文本字符的哈夫曼编码,显示压缩后的结果,并保存至一个新的二进制文件中。
(3)提供恢复文件与原文件的相同性对比功能,以验证恢复的正确性。
(我是中南民族大学2019级学生,请勿抄袭,以防重复!)
#include<iostream>
#include<fstream> //文件读写
#include<string.h>
#include<stdio.h>
#include <windows.h> //头文件Windows.h,可调用Windows库函数,是系统更加人性化
using namespace std;
typedef struct //--------功能:保存出现字符种类的信息---------------
{
string character; //存放文本中的字符
string Binary; //字符转化为的二进制串
int weight; //字符的权值
int parent, lchild, rchild; //双亲,左孩子,右孩子
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;
typedef struct //---------功能:保存文本信息-----------------
{
HTNode* R; //对象
int length; //表长度
}MAD;
//函数定义
int CreatHTNode(MAD& L,string br[3000],char H[3000]) //统计函数,L是结构体MAD的引用,br[]从缓冲区保存文本信息,H[]是文本信息的形参
{
string a[3000]; //保存文字的种类
int y[3000]; //计算该文字出现的权值
for (int j=0; j<2999; j++)
y[j]= 1;
char c;
int i = 0, s = 0,r=0, x = 1, fleg;
while ((c = H[r])!= '*') //将缓存区buffer数组的文本分别进行统计,直到文本最后
{
fleg = 0; //fleg的作用是判断该字符是否在之前出现过
br[i] = c; //数组br[]存放文本内容
for (int m = 1;m <= x;++m) //循环判断统计
{
if (a[m] == br[i]) //如果a[]中的一个文字和缓存区的文字一直,判断相同
{
y[m] += 1; // 出现次数加1
fleg = 1; //判读改文字已经保存在数组中
break;
}
}
if (fleg == 0) //缓存区的该文字第一次出现,没有保存在数组中
{
a[x] = br[i]; //将其保存在数组中
x++;
}
i++; //自增实现条件循环
r++;
}
int h; //x是叶子节点,2*x-1就是哈夫曼树节点总数
h = 2 * x - 1;
L.R = new HTNode[h + 1];
L.length = x; //
for (int f = 1;f <= x;f++)
{
L.R[f].character = a[f]; //将文本字符a[]存入结构体,character保存的值文本字符
L.R[f].weight = y[f]; //同理,t[]保存的是该字符串所出现的次数,警惕保存到结构体当中去
}
return i; //函数返回int类型i,传到主函数的s,保存的是文本的长度
}
void Select(MAD&L, int len, int& s1, int& s2) //在L中查找两个最小值,在构建哈夫曼树的时候调用
{
int i, min1 = 0x3f3f3f3f, min2 = 0x3f3f3f3f; //定义两个最大值min1 and min2
for (i = 1;i <= len;i++)
{
if (L.R[i].weight < min1 && L.R[i].parent == 0) //通过循环结构实现找到结构体当中权值(出现次数)最小的
{
min1 = L.R[i].weight; //将其值赋值给min1
s1 = i; //将其所处的位置传给s1
}
}
int temp = L.R[s1].weight; //
L.R[s1].weight = 0x3f3f3f3f;
for (i = 1;i <= len;i++)
{
if (L.R[i].weight < min2 && L.R[i].parent == 0) //与上文同理
{
min2 = L.R[i].weight; //将其权值赋值给min2,位置i传给s2
s2 = i;
}
}
L.R[s1].weight = temp; //
}
void CreatHuffmanTree(MAD&L) //哈夫曼树插入函数,最优二叉树
{
int n;
n = L.length;
int m, s1, s2, i; //长度
if (n <= 1) return;
m = 2 * n - 1;
for (i = 1;i <= m;++i)
{
L.R[i].parent = 0; L. R[i].lchild = 0; L.R[i].rchild = 0; //双亲左右孩子都初始化为0
}
for (i = n + 1;i <= m;++i) //
{
Select(L, i - 1, s1, s2); //调用select函数,查找最小值
L.R[s1].parent = i; //建立哈夫曼树
L.R[s2].parent = i;
L.R[i].lchild = s1; //左右孩子
L.R[i].rchild = s2;
L.R[i].weight = L.R[s1].weight + L.R[s2].weight; //i 的权值为左右孩子权值之和
}
}
void CreatHuffmanCode(MAD L, HuffmanCode& HC) //通过构建好的哈夫曼树,对叶子节点进行编码
{
int i, start, c, f; //
int n;
n = L.length;
HC = new char* [n];
char* cd = new char[n];
cd[n - 1] = '\0'; //初始化\0,临时保存编码
for (i = 1;i <= n;++i)
{
start = n - 1;
c = i;
f = L.R[i].parent;
while (f != 0) //根节点
{
--start;
if (L.R[f].lchild == c)
cd[start] = '0'; //左0右1
else
cd[start] = '1';
c = f;
f = L.R[f].parent;
}
HC[i] = new char[n - start];
strcpy(HC[i], &cd[start]); //数组H[]保存哈夫曼编码,传值给结构体
L.R[i].Binary = HC[i];
}
delete cd; //清除cd
}
void showHuffmanCode(MAD&L, HuffmanCode HC) //
{
cout<<"文字"<<" "<<"哈夫曼编码"<<" "<<"出现次数"<<endl;
for (int i = 1;i <L.length;++i) //
{
cout << L.R[i].character << " " << L.R[i].Binary << " " << L.R[i].weight << endl;
}
}
void Compressor(MAD& L, string b[],string o[],int s) //文件压缩函数,将生成的哈夫曼编码通过文件读写的方式,写到写到压缩的文件中去
{
ofstream OutFile("D:/mydesk/压缩文件.dat",ios::out); //利用构造函数创建txt文本,并且打开该文本
for (int q = 0;q <= s;q++)
{
for (int u = 0; u <= L.length - 1;u++)
{
if (L.R[u].character == b[q]) //
{
o[q] = L.R[u].Binary;
OutFile << L.R[u].Binary; //把字符串二进制编码内容,写入压缩包.dat文件
}
}
}
OutFile.close(); //关闭Test.txt文件
cout<<"压缩成功!放置在本机桌面上!"<<endl;
}
void UnZip(MAD& L, string b[],string o[],int s) //文件解压函数,通过保存文本信息的str[]去遍历得到相应的文字
{
int sysm=0;
ofstream OutFile("D:/mydesk/解压文件.txt",ios::out); //利用构造函数创建txt文本,并且打开该文本
for (int q = 0;q <= s;q++)
{
for (int u = 0; u <= L.length - 1;u++)
{
if (L.R[u].character == b[q]) //判断条件
{
o[q] = L.R[u].Binary;
sysm=1; //判断解压结果与文本是否一致,不一致直接跳出循环,打印解压失败
OutFile << L.R[u].character; //把得到的字符写到文件中去
}
}
if(sysm==0) break;
}
if(sysm=0) cout<<"压缩出错!"<<endl; //判断是否压缩无误
else cout<<"压缩结果无误"<<endl;
OutFile.close(); //关闭刚才打开文件
cout<<"解压成功!放置在本机桌面上!"<<endl;
}
int main()
{
char fileName[20]; //定义一个字符数组filename[],其保存的是要经过解压操作的源文件的名称
cout<<"输入文件名:";
cin>>fileName; //通过键盘输入
while(1)
{
MAD L;int s; //定义一个结构体对象
HuffmanCode HC; //
string str[3000],o[3000]; //str[]数组可以保存缓冲区文本信息,方便调用后面的函数
char buffer[3000]; //打开filename文件
ifstream in(fileName);
if(!in.is_open()) //文件打开异常反馈
{
cout<<"Error while opening file.";
return 1;
}
while(!in.eof()) //将文件中的文本信息一一读到缓冲区buffer[]
{
in.getline(buffer,3000);
}
in.close(); //文件filename调用完成之后及时关闭文件
s=CreatHTNode(L,str,buffer); //统计
CreatHuffmanTree(L); //调用哈夫曼树构建函数,将L插入哈夫曼树当中
CreatHuffmanCode(L, HC); //调用函数对哈夫曼树的叶子节点进行编码
cout<<"------------------------------------------------欢迎使用小猿压缩程序---------------------------------------------------\n"<<endl;
cout<<"为您提供以下使用服务:"<<endl;
cout<<" 1.压缩文件生成压缩包"<<endl;
cout<<" 2.压缩包解压"<<endl;
cout<<" 3.查看压缩编码"<<endl;
cout<<" 0.退出程序"<<endl;
cout<<"选择序号:"<<endl; //菜单打印
int number; //用户通过数字,可选择调用对应函数
cin>>number;
switch(number) //switch结构
{
//根据用户的需求,选择相对应的函数实现其功能
case 1: system("CLS");Compressor(L,str,o,s);system("pause");break;
case 2: system("CLS");UnZip(L,str,o,s);system("pause");break;
case 3: system("CLS");showHuffmanCode(L, HC);system("pause");break;
case 0 :system("CLS");cout<<"谢谢使用,祝您生活愉快!"<<endl;system("date/t");exit(0);system("pause");
default:system("CLS");cout<<"输入有误!"<<endl;system("pause");
}
}
return 0;
}