起源:在匹配网页中的关键词的时候,发现网页中存在某个目标关键词,但是没有成功匹配出来。经分析发现网页中的关键词是以html实体编码的方式存在的,所以封装一下html实体编码与正常文本之间的转换,也给其他遇到这种问题的朋友们一个参考。
举例:“免费”与“免费”之间的转换
转换过程:
- html实体编码转换为正常文本
- 逐个读取出目标文本中的html实体编码,这算是一个“免”
- 先转换为unicode字符串:\u514D
- 再转为汉字:免
- 正常文本转换为html实体编码
- 逐个取出目标文本中的字,一个汉字算一个字,一个英文字符也算是一个字,一个数字也算是一个字
- 对每个字先转为unicode字符串
- 再由unicode字符串转为html实体编码
#pragma once
#include <string>
#include <boost/locale.hpp>
namespace HtmlEscape {
std::string html_escape(std::string str);
std::string html_unescape(std::string str);
std::string ustr_word_to_char(std::string ustr);
std::string char_word_to_ustr(std::string str_char);
std::string ustr_word_to_html(std::string ustr);
std::string html_word_to_ustr(std::string html_str);
}
// html实体编码 解码为 正常文本字符 最终输出为GBK编码
std::string HtmlEscape::html_unescape(std::string html_str) {
std::string text = html_str;
size_t npos = 0;
while (npos < text.length() - 3) {
if (text[npos] == '&' && text[npos + 1] == '#') {
size_t nend = npos + 2;
while (nend < text.length()) {
if (text[nend] >= '0' && text[nend] <= '9') nend++;
else if (text[nend] == ';') break;
else break;
}
if (text[nend] == ';') {
// 到这里说明定位到一个html实体编码
std::string html_word = text.substr(npos, nend - npos + 1);
std::string uword = HtmlEscape::html_word_to_ustr(html_word);
std::string sword = HtmlEscape::ustr_word_to_char(uword);
// printf("%s -> %s\n", html_word.c_str(), sword.c_str());
if (sword.length() > 0) {
// 替换文本中的html实体编码
std::string tmp = text.substr(0, npos);
tmp += sword;
tmp += text.substr(nend + 1);
text = tmp;
}
}
}
npos += 1;
}
return text;
// std::string str_charset = "";
// if (charset == HtmlEncode_GBK) str_charset = "gbk";
// else if (charset == HtmlEncode_UTF8) str_charset = "utf-8";
// else return "";
//
// size_t nstart = 0, npos = 0;
// while (true) {
// // 定位查找一个html实体编码
// npos = str.find("&#", nstart);
// if (npos == std::string::npos) break;
// size_t nend = npos + 2;
// while (nend < str.length() && str[nend] >= '0' && str[nend] <= '9') nend += 1;
//
// // 定位到一个html实体编码
// if (nend < str.length() && str[nend] == ';') {
// char loginfo[1024] = { 0 };
// // 当前实体编码中的数值
// int html_code_num = atoi(str.substr(npos + 2, nend - npos - 2).c_str());
// sprintf(loginfo, "%d", html_code_num);
// // 数值转Unicode字符串
// std::string charArr = "0123456789ABCDEF";
// std::string unicode_str = "";
// while (true) {
// if (html_code_num < 15) { unicode_str = charArr[html_code_num] + unicode_str; break; }
// else {
// int yushu = html_code_num % 16;
// html_code_num = html_code_num / 16;
//
// unicode_str = charArr[yushu] + unicode_str;
// }
// }
// sprintf(loginfo, "%s %s", loginfo, unicode_str.c_str());
// // Unicode字符串 转 文本字符
// std::string sword = HtmlEscape::ustr_word_to_char(unicode_str);
// sprintf(loginfo, "%s %s", loginfo, sword.c_str());
// // printf("%s\n", loginfo);
//
// // 获取到的文本字符 更新到字符串中
// std::string tmp = str.substr(0, npos);
// tmp += sword;
// tmp += str.substr(nend + 1);
// str = tmp;
// }
// else nstart = npos + 2;
// }
// return str;
}
// 正常文本字符 转码为 html实体编码 输入文本为GBK编码
std::string HtmlEscape::html_escape(std::string str) {
std::string html_str = "";
for (size_t i = 0; i < str.length(); i++) {
int byte_num;
if ((unsigned char)str[i] <= 0x80) byte_num = 1;
else byte_num = 2;
std::string sword = str.substr(i, byte_num);
std::string uword = HtmlEscape::char_word_to_ustr(sword);
std::string html_word = HtmlEscape::ustr_word_to_html(uword);
i += (byte_num - 1);
if (html_word.length() > 0) html_str += html_word;
else html_str += sword;
}
return html_str;
}
// 只处理一个字 \u514D 转为 “免” (输出结果为GBK编码)
std::string HtmlEscape::ustr_word_to_char(std::string ustr) {
std::wstring wstr;
if (ustr.substr(0, 2) == "\\u") ustr = ustr.substr(2);
if (ustr.length() == 0) return "";
wchar_t ws[2] = { 0 }; ws[0] = strtol(ustr.c_str(), NULL, 16);
std::string char_str = boost::locale::conv::from_utf(ws, "GBK");
return char_str;
}
// 只处理一个字 “免” 转为 \u514D (传入值为GBK编码)
std::string HtmlEscape::char_word_to_ustr(std::string str_char) {
std::string ustr = "";
std::wstring wstr = boost::locale::conv::to_utf<wchar_t>(str_char, "GBK");
for (size_t i = 0; i < wstr.length() * sizeof(wchar_t); i++) {
char tmp[5] = { 0 };
sprintf(tmp, "%X", *((unsigned char*)wstr.c_str() + i));
ustr = tmp + ustr;
}
ustr = "\\u" + ustr;
return ustr;
}
// Unicode字符串 转 html实体编码 \u514D 转 免
std::string HtmlEscape::ustr_word_to_html(std::string ustr) {
if (ustr.substr(0, 2) == "\\u") ustr = ustr.substr(2);
int unicode_num = stoul(ustr, 0, 16);
/*
// 下面是stoul的实现
std::string charArr = "0123456789ABCDEF";
int html_code_num = 0;
for (int i = 0; i < unicode_str.length(); i++) {
char ch = unicode_str[unicode_str.length() - i - 1];
int num = 0;
while (num < charArr.length() && charArr[num] != ch) num++;
html_code_num += (num*pow(16, i));
}
*/
char tmp[10] = { 0 }; sprintf(tmp, "&#%d;", unicode_num);
std::string html_word = tmp;
return html_word;
}
// html实体编码 转 Unicode字符串 免 转 \u514D
std::string HtmlEscape::html_word_to_ustr(std::string html_str) {
if (html_str.length() <= 3 || html_str.substr(0, 2) != "&#" || html_str[html_str.length() - 1] != ';') return "";
for (size_t i = 2; i < html_str.length() - 1; i++) {
if (html_str[i] < '0' || html_str[i] > '9') return "";
}
// 到这里确认当前html实体编码是正确格式
int html_word_num = atoi(html_str.substr(2, html_str.length() - 1 - 2).c_str());
std::string charArr = "0123456789ABCDEF";
std::string unicode_str = "";
while (true) {
if (html_word_num < 15) { unicode_str = charArr[html_word_num] + unicode_str; break; }
else {
int yushu = html_word_num % 16;
html_word_num = html_word_num / 16;
unicode_str = charArr[yushu] + unicode_str;
}
}
unicode_str = "\\u" + unicode_str;
return unicode_str;
}