将 utf8 实体从 json 解码为 utf8 C++

Decode utf8 entities from json into utf8 C++

本文关键字:utf8 C++ 解码 实体 json      更新时间:2023-10-16

我有一个带有utf8实体的字符串(我不确定我的名字是否正确):

std::string std = "u0418u043du0434u0435u043au0441";

如何将其转换为更具可读性的内容?我使用支持 C++11 的 g++,但是在挖掘 std::codecvt 手册几个小时后,我没有得到任何结果:

std::string std = "u0418u043du0434u0435u043au0441";
wstring_convert<codecvt_utf8_utf16<char16_t>,char16_t> convert; 
string dest = convert.to_bytes(std); 

返回以以下内容开头的噩梦堆栈跟踪:

error: no matching function for call to ‘std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>::to_bytes(std::string&)

我希望还有另一种方式。

首先,你对std::wstring_convert的使用是倒退的。 您有一个 UTF-8 编码的std::string,要将其转换为宽 Unicode 字符串。 您收到编译器错误,因为to_bytes()不将std::string作为输入。 它需要一个std::wstring_convert::wide_string作为输入(这在您的情况下是std::u16string,因为您在专业化中使用了char16_t),因此您需要使用from_bytes()而不是to_bytes()

std::string std = "u0418u043du0434u0435u043au0441";
std::wstring_convert<codecvt_utf8_utf16<char16_t>, char16_t> convert; 
std::u16string dest = convert.from_bytes(std);

话虽如此,JSON 规范的第 9 节指出:

9 字符串

字符串是用引号 (U+0022) 括起来的 Unicode 代码点序列。除必须转义的字符外,所有字符都可以放在引号内:引号 (U+0022)、反固线 (U+005C) 和控制字符 U+0000 到 U+001F。某些字符的转义序列表示形式为两个字符。

"表示引号字符 (U+0022)。

\表示反向固相线字符 (U+005C)。

/表示固线字符 (U+002F)。

b表示退格符 (U+0008)。

f表示表单馈送字符 (U+000C)。

n表示换行符 (U+000A)。

r表示回车符 (U+000D)。

t表示字符制表字符 (U+0009)。

因此,例如,仅包含一个反向实线字符的字符串可以表示为"\"。

任何代码点都可以表示为十六进制数。这种数字的含义由 ISO/IEC 10646 确定。如果代码点位于基本多语言平面(U+0000 到 U+FFFF)中,则可以表示为六个字符的序列:反向固相线,后跟小写字母u,后跟对代码点进行编码的四个十六进制数字。十六进制数字可以是数字(U+0030 到 U+0039)或大写AF的十六进制字母(U+0041 到 U+0046)或小写(U+0061 到 U+0066)。因此,例如,仅包含一个反向固线字符的字符串可以表示为"u005C"。

以下四种情况都产生相同的结果:

"u002F">

"u002f">

"/">

"/">

为了转义不在基本多语言平面中的代码点,该字符表示为 12 个字符的序列,对 UTF-16 代理项对进行编码。例如,仅包含G谱号字符(U+1D11E)的字符串可以表示为"uD834uDD1E"。

原始 JSON 数据本身可以用 UTF-8(最常见的编码)、UTF-16 等进行编码。 但无论使用何种编码,字符序列"u0418u043du0434u0435u043au0441"都表示 UTF-16 代码单元序列U+0418 U+043d U+0434 U+0435 U+043a U+0441,即 Unicode 字符串"Индекс"

如果您使用实际的 JSON 解析器(例如 JSON for Modern C++、jsoncpp、RapidJSON 等),它将为您解析 UTF-16 代码单元值并返回可读的 Unicode 字符串。

但是,如果您手动处理 JSON 数据,则必须手动解码任何xuXXXX转义序列。std::wstring_convert不能为你做到这一点。 它只能将 JSON 从std::string转换为std::wstring/std:::u16string,如果这使您可以更轻松地解析数据。 但是,您仍然必须单独分析 JSON 的内容

之后,如果需要,您可以使用std::wstring_convert将任何提取的std::wstring/std::u16string字符串转换回UTF-8以节省内存。

您看到的不是实体,而是代码点。您通过 Unicode 转义序列定义字符,编译器会自动将它们转换为 UTF-8。将其转换为 UTF-16 反之亦然的典型方法是:

static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::string ws2s(const std::wstring &wstr) {
std::string narrow = converter.to_bytes(wstr);
return narrow;
}
std::wstring s2ws(const std::string &str) {
std::wstring wide = converter.from_bytes(str);
return wide;
}

当然,您不能将原始字符串转换为另一个相同类型的字符串(std::string),因为它不能容纳此类字符。这就是为什么 UTF-16 代码首先由编译器转换为 UTF-8 的原因。