C++ 将 ASII 转义的 unicode 字符串转换为 utf8 字符串
C++ convert ASII escaped unicode string into utf8 string
我需要读入带有 unicode 转义的标准 ascii 样式字符串,并将其转换为包含 utf8 编码等效项的 std::string。因此,例如“\u03a0”(具有 6 个字符的 std::string)应转换为具有两个字符的 std::string,分别为原始二进制文件中的 0xce、0xa0。
如果有一个使用 icu 或 boost 的简单答案,我将非常高兴,但我找不到。
(这类似于 Convert a Unicode string to an escaped ASCII string,但请注意,我最终需要达到 UTF8 编码。如果我们可以使用 Unicode 作为中间步骤,那很好。)
(\u03a0是UTF-8编码为0xCE 0xA0的希腊大写字母PI的Unicode编码点)
您需要:
- 从字符串“\u03a0”中获取数字 0x03a0:删除反斜杠和 u 并将 03a0 解析为十六进制,转换为 wchar_t。重复直到你得到一个(宽)字符串。
- 将 0x3a0 转换为 UTF-8。 C++11 有一个 codecvt_utf8 可能有帮助。
尝试这样的事情:
std::string to_utf8(uint32_t cp)
{
/*
if using C++11 or later, you can do this:
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
return conv.to_bytes( (char32_t)cp );
Otherwise...
*/
std::string result;
int count;
if (cp <= 0x007F)
count = 1
else if (cp <= 0x07FF)
count = 2;
else if (cp <= 0xFFFF)
count = 3;
else if (cp <= 0x10FFFF)
count = 4;
else
return result; // or throw an exception
result.resize(count);
if (count > 1)
{
for (int i = count-1; i > 0; --i)
{
result[i] = (char) (0x80 | (cp & 0x3F));
cp >>= 6;
}
for (int i = 0; i < count; ++i)
cp |= (1 << (7-i));
}
result[0] = (char) cp;
return result;
}
std::string str = ...; // "\u03a0"
std::string::size_type startIdx = 0;
do
{
startIdx = str.find("\u", startIdx);
if (startIdx == std::string::npos) break;
std::string::size_type endIdx = str.find_first_not_of("0123456789abcdefABCDEF", startIdx+2);
if (endIdx == std::string::npos) break;
std::string tmpStr = str.substr(startIdx+2, endIdx-(startIdx+2));
std::istringstream iss(tmpStr);
uint32_t cp;
if (iss >> std::hex >> cp)
{
std::string utf8 = to_utf8(cp);
str.replace(startIdx, 2+tmpStr.length(), utf8);
startIdx += utf8.length();
}
else
startIdx += 2;
}
while (true);
我的解决方案:
convert_unicode_escape_sequences(str)
input: "\u043f\u0440\u0438\u0432\u0435\u0442"
output: "привет"
用于 wchar/chars 转换的提升:
#include <boost/locale/encoding_utf.hpp>
using boost::locale::conv::utf_to_utf;
inline uint8_t get_uint8(uint8_t h, uint8_t l)
{
uint8_t ret;
if (h - '0' < 10)
ret = h - '0';
else if (h - 'A' < 6)
ret = h - 'A' + 0x0A;
else if (h - 'a' < 6)
ret = h - 'a' + 0x0A;
ret = ret << 4;
if (l - '0' < 10)
ret |= l - '0';
else if (l - 'A' < 6)
ret |= l - 'A' + 0x0A;
else if (l - 'a' < 6)
ret |= l - 'a' + 0x0A;
return ret;
}
std::string wstring_to_utf8(const std::wstring& str)
{
return utf_to_utf<char>(str.c_str(), str.c_str() + str.size());
}
std::string convert_unicode_escape_sequences(const std::string& source)
{
std::wstring ws; ws.reserve(source.size());
std::wstringstream wis(ws);
auto s = source.begin();
while (s != source.end())
{
if (*s == '\')
{
if (std::distance(s, source.end()) > 5)
{
if (*(s + 1) == 'u')
{
unsigned int v = get_uint8(*(s + 2), *(s + 3)) << 8;
v |= get_uint8(*(s + 4), *(s + 5));
s += 6;
wis << boost::numeric_cast<wchar_t>(v);
continue;
}
}
}
wis << wchar_t(*s);
s++;
}
return wstring_to_utf8(wis.str());
}
我需要读入带有 unicode 转义的标准 ascii 样式字符串,并将其转换为包含 utf8 编码等效项的 std::string。因此,例如“\u03a0”(具有 6 个字符的 std::string)应转换为具有两个字符的 std::string,分别为原始二进制文件中的 0xce、0xa0。
如果有一个使用 icu 或 boost 的简单答案,我将非常高兴,但我找不到。
(这类似于 Convert a Unicode string to an escaped ASCII string,但请注意,我最终需要达到 UTF8 编码。如果我们可以使用 Unicode 作为中间步骤,那很好。)
(\u03a0是UTF-8编码为0xCE 0xA0的希腊大写字母PI的Unicode编码点)
您需要:
- 从字符串“\u03a0”中获取数字 0x03a0:删除反斜杠和 u 并将 03a0 解析为十六进制,转换为 wchar_t。重复直到你得到一个(宽)字符串。
- 将 0x3a0 转换为 UTF-8。 C++11 有一个 codecvt_utf8 可能有帮助。
尝试这样的事情:
std::string to_utf8(uint32_t cp)
{
/*
if using C++11 or later, you can do this:
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
return conv.to_bytes( (char32_t)cp );
Otherwise...
*/
std::string result;
int count;
if (cp <= 0x007F)
count = 1
else if (cp <= 0x07FF)
count = 2;
else if (cp <= 0xFFFF)
count = 3;
else if (cp <= 0x10FFFF)
count = 4;
else
return result; // or throw an exception
result.resize(count);
if (count > 1)
{
for (int i = count-1; i > 0; --i)
{
result[i] = (char) (0x80 | (cp & 0x3F));
cp >>= 6;
}
for (int i = 0; i < count; ++i)
cp |= (1 << (7-i));
}
result[0] = (char) cp;
return result;
}
std::string str = ...; // "\u03a0"
std::string::size_type startIdx = 0;
do
{
startIdx = str.find("\u", startIdx);
if (startIdx == std::string::npos) break;
std::string::size_type endIdx = str.find_first_not_of("0123456789abcdefABCDEF", startIdx+2);
if (endIdx == std::string::npos) break;
std::string tmpStr = str.substr(startIdx+2, endIdx-(startIdx+2));
std::istringstream iss(tmpStr);
uint32_t cp;
if (iss >> std::hex >> cp)
{
std::string utf8 = to_utf8(cp);
str.replace(startIdx, 2+tmpStr.length(), utf8);
startIdx += utf8.length();
}
else
startIdx += 2;
}
while (true);
我的解决方案:
convert_unicode_escape_sequences(str)
input: "\u043f\u0440\u0438\u0432\u0435\u0442"
output: "привет"
用于 wchar/chars 转换的提升:
#include <boost/locale/encoding_utf.hpp>
using boost::locale::conv::utf_to_utf;
inline uint8_t get_uint8(uint8_t h, uint8_t l)
{
uint8_t ret;
if (h - '0' < 10)
ret = h - '0';
else if (h - 'A' < 6)
ret = h - 'A' + 0x0A;
else if (h - 'a' < 6)
ret = h - 'a' + 0x0A;
ret = ret << 4;
if (l - '0' < 10)
ret |= l - '0';
else if (l - 'A' < 6)
ret |= l - 'A' + 0x0A;
else if (l - 'a' < 6)
ret |= l - 'a' + 0x0A;
return ret;
}
std::string wstring_to_utf8(const std::wstring& str)
{
return utf_to_utf<char>(str.c_str(), str.c_str() + str.size());
}
std::string convert_unicode_escape_sequences(const std::string& source)
{
std::wstring ws; ws.reserve(source.size());
std::wstringstream wis(ws);
auto s = source.begin();
while (s != source.end())
{
if (*s == '\')
{
if (std::distance(s, source.end()) > 5)
{
if (*(s + 1) == 'u')
{
unsigned int v = get_uint8(*(s + 2), *(s + 3)) << 8;
v |= get_uint8(*(s + 4), *(s + 5));
s += 6;
wis << boost::numeric_cast<wchar_t>(v);
continue;
}
}
}
wis << wchar_t(*s);
s++;
}
return wstring_to_utf8(wis.str());
}