c++哈夫曼编码实现文件的压缩和解压。c++

/*

偶然接触到哈夫曼编码,决定挺有意思,于是也想自己实现一个小的压缩系统。题目如下:
设计一个小系统,通过扫描一个 文件获得文件中相关字符的权重,
以此实现对文件的压缩,并同时构建出相应的解压缩功能,以还原所压缩文件,
以便判断所实现功能的正确性。
*/ 

注意:下边这个系统还有点小毛病,虽然可以正常压缩解压,但是压缩后的哈夫曼编码文件反而比源文件还大的多。 但是构建哈夫曼树和求哈夫曼编码没有错。推测可能是在存储的编码方面出现了问题。由于时间有限,这个坑先留下,等以后有时间了再来填上。而且还有个问题没有很好的解决:就是新的哈夫曼编码长度是不一样的,我这里将哈夫曼编码写入压缩文件的时候采取的是写一个字符换一行,但是由于回车符也会占用好大的空间,所用我猜测造成压缩后文件增大的原因有一部分就是回车符太多。如果有什么好的办法可以留言告诉我。

/*更新*/

找到了问题所在,其实就是因为我在将编码后的二进制串写入文件的时候为了防止读取的时候没办法实现精准分割,于是在每次写入都加上了一个回车,这个回车带来了巨大的空间浪费,由于那时候还没有学离散数学,不知道前缀码这个东西,所以造成了这个bug,现在我们就可以修改程序,挖个坑以后再改,最近有点忙。

#include<iostream>
#include <fstream> 
#include<map>
#include<queue>
#include <string>
using namespace std;
// 二叉树结点
struct HuffmanNode {
	HuffmanNode* lChild= nullptr;   //左孩子,一定要初始化,否则就会出很麻烦的问题。
	HuffmanNode* rChild = nullptr;  //右孩子
	char Data='#';    //存储的字符
	int Weight;   //构建结点的时候存储权重,即频率
	HuffmanNode(char Data, int Weight) {   
		this->Data = Data;
		this->Weight = Weight;
	}
	HuffmanNode() {};
};
// 自定义HuffmanNode比较函数
struct HuffmanNodeCompareWeightGreater
{
	bool operator() (const HuffmanNode *a, const HuffmanNode *b)
	{
		return a->Weight > b->Weight;	// 小顶堆
	}
};

class HuffmanTree {
	HuffmanNode* root;
	map <char, string> HuffmanMap;  //存储了对应字符和编码的map,解压的时候用与对照还原。
	map <char, int> OriginMap; //用于构建哈夫曼树之前统计频率所用,存储的是字符和对应的频率
    				
	//字符频率统计,并存入最初的(字符-编码)map中
	void GiveWeight(char str);
	//构建哈夫曼树,
	void CreateHuffmanTree();
	//文件读取,并统计字符和频率以此来构建OriginMap
	void ReadFile();
	//遍历哈夫曼树所得到的字符以及编码得到新的映射,以字符串s形式存储处理后的Huffman编码
	void DisplayHuffmanTree(HuffmanNode* Temp,string s);
	//Pre为先序遍历,用来检验生成的树是否正确
	void Pre(HuffmanNode* p);
public:
	HuffmanTree() {
		root = new HuffmanNode();
	}
	//文件压缩并写入,对应着源文件和HuffmanMap中的编码一个一个的输出到新的文件
	void ZIP();
	//文件解压,
	void UNZIP();
	
};

//构建哈夫曼树,
void HuffmanTree::CreateHuffmanTree() {
	//遍历原始的map并根据其键值对逐一构建结点
	priority_queue<HuffmanNode*, vector<HuffmanNode*>, HuffmanNodeCompareWeightGreater> HuffmanQueue;
	map<char, int>::iterator it = OriginMap.begin();
	while (it != OriginMap.end()) {
		//这个每循环一次就取出一组键和值
		char ch = it->first;
		int frequency = it->second;
		HuffmanNode *temp=new HuffmanNode(ch, frequency);  //构建结点,ch即为每个节点存储的字符
		HuffmanQueue.push(temp);
		it++;
	}
	if(!HuffmanQueue.empty())
	if (HuffmanQueue.size() == 1) 
		root->lChild = HuffmanQueue.top();
	else if(HuffmanQueue.size() > 1)
		while (HuffmanQueue.size()!=1) {
		HuffmanNode *hfNode_01 = HuffmanQueue.top();
		HuffmanQueue.pop();
		HuffmanNode *hfNode_02 = HuffmanQueue.top();
		HuffmanQueue.pop();
		HuffmanNode *SumNode=new HuffmanNode('#',hfNode_01->Weight+hfNode_02->Weight);
		//权重小的放在左孩子位置
		if (hfNode_01->Weight < hfNode_02->Weight)
		{
			SumNode->lChild = hfNode_01;
			SumNode->rChild = hfNode_02;
		}
		else {
			SumNode->lChild = hfNode_02;
			SumNode->rChild = hfNode_01;
		}
		HuffmanQueue.push(SumNode);
		root = SumNode;
	}
	cout << "哈夫曼处理后的新编码为: " << endl;
	//Pre(root); //检验生成的哈夫曼树是否正确
	//构建哈夫曼字符编码映射HuffmanMap
	string s;
	DisplayHuffmanTree(root,s);

}

void HuffmanTree::DisplayHuffmanTree(HuffmanNode* Temp,string s) {
	if (Temp != nullptr) {
		if (Temp->Data != '#') {
			HuffmanMap.insert(pair<char, string>(Temp->Data, s));
			cout <<Temp->Data<<" "<<s << "  " << endl;
		}
		DisplayHuffmanTree(Temp->lChild, s.append("0"));
		s.pop_back();      //需要删除上一个递归语句在结尾加上的数字,否则他会影响下面的递归语句
		DisplayHuffmanTree(Temp->rChild, s.append("1"));
	}	
}

void HuffmanTree::GiveWeight(char str) {
	//如果OriginMap不含该字符,就将该字符插入map并频率设置为1
	if (OriginMap.count(str) == 0) {
		OriginMap.insert(pair<char, int>(str, 1));
	}
	//如果OriginMap含有该字符,就将该字符的频率更新+1
	else {
		int frequency = OriginMap[str];
		++frequency;
		OriginMap.erase(str);
		OriginMap.insert(pair<char, int>(str, frequency));
	}
}
//读文件并统计频率
void HuffmanTree::ReadFile() {
	ifstream ifs("C:\\Users\\DELL\\Desktop\\压缩文件\\待压缩的文件.txt");
	char ch;
	cin.unsetf(ios::skipws);
	if (!ifs.is_open())
		cout << "文件打开失败" << endl;
	while (ifs.get(ch)) 
		GiveWeight(ch);  // 统计频率
	ifs.close();
}
void HuffmanTree::Pre(HuffmanNode* p) {
	if (p != NULL) {
		cout << p->Weight << " ";
		Pre(p->lChild);
		Pre(p->rChild);
	}
}
void HuffmanTree::ZIP() {
	ReadFile();
	CreateHuffmanTree();
	//对照源文件,把新的编码再存入文件(待完成)
	ifstream ifs("C:\\Users\\DELL\\Desktop\\压缩文件\\待压缩的文件.txt");
	ofstream ofs("C:\\Users\\DELL\\Desktop\\压缩文件\\压缩后的文件.txt");
	if (!ifs.is_open() || !ofs.is_open()) {
		cout << "文件打开失败";
		return;
	}
	char ch;
	cin.unsetf(ios::skipws);
	while (ifs.get(ch))
		ofs << HuffmanMap.find(ch)->second << endl;
	
	ifs.close();
	ofs.close();

	ofstream ofsHuffmanCode("C:\\Users\\DELL\\Desktop\\压缩文件\\字符和生成的哈夫曼编码.txt");
	map<char, string > ::iterator it = HuffmanMap.begin();
	while (it!=HuffmanMap.end()) {
		ofsHuffmanCode << it->first << " " << it->second << endl;
		it++;
	}
}

void HuffmanTree::UNZIP() {
	ifstream in("C:\\Users\\DELL\\Desktop\\压缩文件\\压缩后的文件.txt");
	string line;
	ofstream out("C:\\Users\\DELL\\Desktop\\压缩文件\\解压后的文件.txt");
	if (in.is_open()) 
	{
		while (getline(in, line))
		{
			//通过value找 key
			for (map<char, string>::iterator it = HuffmanMap.begin(); it != HuffmanMap.end(); it++)
			{
				if (it->second == line)
					out << it->first;
			}
		}
	}
	else // 没有该文件
	{
		cout << "文件打开失败" << endl;
	}
	in.close();
	out.close();
}

int main() {
	HuffmanTree hfTree;
	hfTree.ZIP();
	hfTree.UNZIP();
}

  • 2
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ad_m1n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值