目录
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:空间域名、虚拟空间、营销软件、网站建设、沾益网站维护、网站推广。一、什么是哈夫曼编码?
二、utf-8编码
三、压缩流程
1.哈夫曼节点及汉字判断
2.获取字符权重
3.根据权重构造哈夫曼树
4.根据哈夫曼树构造译码表
5.压缩
四、解压缩
五、效果展示
一、什么是哈夫曼编码?
哈夫曼编码,又称为哈夫曼编码(Huffman Coding)是一种可变长编码( VLC, variable length coding))方式,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码。
二、utf-8编码UTF-8是Unicode的实现方式之一 并不是唯一 也不等同于Unicode。除了UTF-8 还有UTF-16和UTF-32 只是很少被使用。
UTF-8的特点是对不同范围的字符使用不同长度的编码 它可以使用1~4个字节表示一个符号 根据不同的符号而变化字节长度。
其编码规则很简单对于单字节的符号 ,字节的第一位设为0 ,后面7位为这个符号的Unicode码。因此对于英语字母 UTF-8编码和ASCII码是相同的。对于n字节的符号,其第一个字节的前n位都设为1 第n+1位设为0 后面字节的前两位一律设为10 剩下的没有提及的二进制位 全部为这个符号的Unicode码。
三、压缩流程 1.哈夫曼节点及汉字判断在utf-8中一个英文字符占一个字节,用一个char表示足矣,但对中文而言,汉字占了三个字节,所以在这里我采用c++string类来统一表示中英文。
typedef struct huffmannode {
std::string ch;
int weight = 0;
int father = 0;
int lc = 0, rc = 0;
}huffmanNode;
在读取文件之前我们要先判断所读取的字符是汉字还是英文,由上面的utf-8编码规则我们可以知道汉字的第一个字节的编码一定是 1110 xxxx 而英文字符的编码一定是 0xxx xxxx,所以我们只要判断字节的第一位是不是1就行。
bool judgeEng(unsigned char c)
{
if (!(c & 0x80))
return true;
return false;
}
2.获取字符权重在判断一个字符是否为汉字后,如果是汉字,就接着再读两个字节,这三个连着的字节就是一个汉字。按此方法将文本遍历到结尾,统计出每个字符的出现次数。(为方便,在这里我用了map)
void getWeightMap(ifstream& fin, const char* fileName, map& _weightMap)
{
fin.open(fileName, std::ios::in);
unsigned char c;
std::string s;
while (fin.peek()!=EOF)
{
c = fin.get();
s = "";
if (judgeEng(c))
s += c;
else
{
s += c;
c = fin.get();
s += c;
c = fin.get();
s += c;
}
_weightMap[s]++;
}
fin.close();
}
3.根据权重构造哈夫曼树void getHuffmanTree(map& _weightMap, vector& _huffmanTree)
{
int n = _weightMap.size();
for (auto it : _weightMap) {
huffmannode t;
t.ch = it.first;
t.weight = it.second;
_huffmanTree.push_back(t);
}
int s1 = 0, s2 = 0;
for (int i = n; i< 2 * n - 1; ++i) {
huffmannode t;
selectTwo(_huffmanTree, i, s1, s2); //从前i个选出两个权重最小的俩个
_huffmanTree.at(s1).father = i;
_huffmanTree.at(s2).father = i;
t.lc= s1;
t.rc = s2;
t.weight = _huffmanTree.at(s1).weight + _huffmanTree.at(s2).weight;
_huffmanTree.push_back(t);
}
}
4.根据哈夫曼树构造译码表void getPassworld(vector& _huffmanTree, map& _passworldMap)
{
int n = (_huffmanTree.size() + 1) / 2;
for (int i = 0; i< n; ++i) {
std::string passworld = "";
int t = i;
int fa = _huffmanTree.at(i).father;
while (fa) {
if (_huffmanTree.at(fa).lc == t)
passworld = '0' + passworld;
else if (_huffmanTree.at(fa).rc == t)
passworld = '1' + passworld;
t = fa;
fa = _huffmanTree.at(t).father;
}
_passworldMap.emplace(huffmanTree.at(i).ch, passworld);
}
}
5.压缩得到译码表后,将原文的每个字符(包括中文)翻译成对应的不定长二进制字符串,然后以8位为一个uchar再重新输入到另一个文件中即为压缩后的文件。
//将原文翻译成二进制字符串
std::string binary = "";
fin.open(fileName, std::ios::in);
unsigned char c;
binary = "";
while (fin.peek()!=EOF)
{
c = fin.get();
std::string s = "";
if (judgeEng(c))
s += c;
else
{
s += c;
c = fin.get();
s += c;
c = fin.get();
s += c;
}
binary+=passworldMap[s];
}
fin.close();
//将得到的二进制字符串每8位转为一个uchar类型写入压缩文件
for (int i = 0; i< binary.size(); i += 8)
{
std::string k = binary.substr(i, 8);
c = binaryStringToChar(k);
fout<< c;
}
在这个过程中我们需要解决两个问题:
1.原文翻译后的二进制字符串长度不是8的倍数
这个问题很简单,不足8位在末尾补0或者1都行。
//在末尾补0
int add0Num = binary.size() % 8;
if (add0Num)
add0Num = 8 - add0Num;
for (int i = 0; i< add0Num; ++i, binary += '0');
2.压缩文件如何解压
因为需要对压缩文件解压,所以我们在压缩时还需要加入一些辅助信息,比如哈夫曼树的权重图和上个问题中末尾补0或1的个数都是我们解压时需要的。所以在压缩时这些信息也需要一同放入压缩文件中。
// 将 辅助信息写入压缩文件中
fout<< weightMap.size()<< " "<
四、解压缩解压首先要将压缩文件的辅助信息读出,并利用辅助信息还原哈夫曼树和原文翻译后的二进制字符串,然后根据哈夫曼树将二进制字符串还原。
unsigned char c;
int size, add0;//字符个数和末尾补0的个数
std::map_weightmap;//权重图
fin >>size >>add0;
fin.get();
for (int i = 0; i< size; ++i)//从压缩文件读取原权重图
{
std::string s = "";
int weight = 0;
c=fin.get();
if (judgeEng(c))
s += c;
else
{
s += c;
c = fin.get();
s += c;
c = fin.get();
s += c;
}
c=fin.get();
c = fin.get();
for (; c!= ' '; c=fin.get())
{
weight = weight * 10 + c - '0';
}
_weightmap.emplace(s, weight);
}
//还原哈夫曼树
std::vector_huffmantree;
getHuffmanTree(_weightmap, _huffmantree);
std::string binary = "";
while (fin.peek()!=EOF)//将压缩文件的内容转为二进制字符串
{
c = fin.get();
binary += ucharToBinaryString(c);
}
fin.close();
for (int i = 0; i< add0; binary.pop_back(), ++i);//删去压缩时末尾补充的0
五、效果展示原文
压缩
解压后
可以看到解压后的文件和原文一致
完整下载链接
https://download.csdn.net/download/yyl1025/87320473?spm=1001.2014.3001.5501
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
当前题目:c++实现基于哈夫曼编码的文本(中英混合)压缩和解压缩-创新互联
文章链接:http://scgulin.cn/article/ddecip.html