如何将 UTF8 转换为 std::string



我正在处理这段代码,它接收一个 cpprest sdk 响应,其中包含一个 base64_encoded 有效负载,这是一个 json。 这是我的代码片段:

typedef std::wstring string_t; //defined in basic_types.h in cpprest lib
void demo() {
http_response response; 
//code to handle respose ...
json::value output= response.extract_json();
string_t payload = output.at(L"payload").as_string();
vector<unsigned char> base64_encoded_payload = conversions::from_base64(payload);
std::string utf8_payload(base64_encoded_payload.begin(), base64_encoded_payload.end()); //in debugger I see the Japanese chars are garbled.
string_t utf16_payload = utf8_to_utf16(utf8_payload); //in debugger I see the Japanese chars are good here
//then I need to process the utf8_payload which is an xml.
//I have an API available to process the xml which takes an string
processXML(utf16_payload); //need to convert utf16_payload to a string here;
}

我也试过这个,我看到str包含乱码字符!

#include <codecvt>  // for codecvt_utf8_utf16
#include <locale>   // for wstring_convert
#include <string>   // for string, wstring
void wstr2str(void) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conversion;
std::wstring japanese = L"北島 美奈";
std::string str = conversion.to_bytes(japanese); //str is garbled:(
}

我的问题是:包含日语字符的 UTF8 可以转换为 std::string 而不会乱码吗?

更新:我获得了对processXML()代码的访问权限,并将输入参数类型更改为std::wstring并且它起作用了。 我想当创建 xml 时,它是将 std::string 转换为 wstring;然而,结果并不好!

void processXML(std::wstring xmlStrBuf) { //chaned xmlStrBuf to wstring and worked
// more code
CComBSTR xmlBuff = xmlStrBuf.c_str(); 
VARIANT_BOOL bSuccess = false;
xmlDoc->loadXML(xmlBuff, &bSuccess);
//more code

}

感谢您的回答,当提到字符串只是一个存储时,它们很有帮助。

你在这里混淆了不同的概念。

存储

这就是我们保存/存储/保存数据的方式。std::string字节char集合。std::wstringwchar_t的集合,有时是 2 字节宽的值(但这不能保证!

编码

这就是数据的含义,以及应该如何解释它。一个std::string,一个字节的集合,可以容纳UTF-8,或UTF-16,或UTF-32,或ASCII,或ShiftJIS,或莫尔斯电码,或JPEG,或电影,或我的DNA(幸运字符串!

世界上有一些强有力的惯例在起作用。例如,在Windows上,通常接受一个std::wstring来保存UTF-16(因为双字节存储很方便,也因为这是Windows API的方式)。

较新版本的C++也为我们提供了std::u16_stringstd::u32_string之类的东西,它们仍然没有任何直接的编码概念,但打算分别用于UTF-16和UTF-32,因为它们的名称使代码读者更加明显。C++20 将引入旨在表示 UTF-8 编码字符串的std::u8_string(否则或多或少类似于std::string)。

但这些只是惯例。关于类型std::string的任何内容都没有说"UTF-8"或任何其他东西。它不知道、不关心或强制执行任何编码。它只存储字节。

因此,您关于"将UTF-8转换为std::string"的问题实际上没有任何意义;这就像问如何将道路转换为汽车。

"那我该怎么办?">

好吧,Base64 也不是编码。嗯,实际上,它完全是,但它是字符串编码之上的编码。这是一种传输/转义/清理原始字节的方法,而不是描述以后如何解释它们的方式。通过要求 cpprest 从 Base64 转换,这只是改变了提供原始字节的方式。这就是为什么它给你一个std::vector<char>而不是一个std::string,因为,虽然(如上所述)std::string不关心编码,但我们有时会使用std::vector<char>来真正、正确地、完整地说"这个集合没有任何特定的编码,所以请不要试图从约定或任何编码中猜测这个用例中的编码是什么;它只知道它是一堆字节"。这取决于意见。有些人仍然会为此使用std::string;CPPREST的作者决定不这样做。

关键是使用该函数from_base64无法告诉我们有关您检索到的文本的编码的任何信息。为此,我们必须回到文本的文档。我们无法获得这一点,您也没有告诉我们任何有关此事。如果它只是一个 JSON 字符串,编码将下降到 cpprest JSON 库,所以你已经完成了。但是,事实并非如此:它是由创建 JSON 对象的人打包到 Base64 表示中的东西。同样,这些信息不是您与我们共享的。

但是,根据您选择的变量名称,您正在查看的数据已经是 UTF-8。然后,您尝试将其转换为 UTF-16,这与您所描述的您想要执行的操作相反。

(同样,在第二个示例中,您已经std::wstring[可能]已经存储了UTF-16,这要归功于L"wide string literal",然后告诉计算机它是UTF-8并将其"再次"转换为UTF-16,然后将原始字节提取到std::string。这些都说不通。

相反,为什么不从字面上processXML(utf8_payload);

一般建议

编码可能相当复杂,尽管一旦你把你的思想都包裹在所有这些抽象层的基本概念上,处理起来就会容易得多。对于未来,对于这个问题,如果你想澄清它,你需要确保你绝对清楚,在数据"管道"的每个阶段,当它从A地传输到B地,并从C型转换为D型,以及其他什么,关于它应该在每个步骤中的编码。如果要在其中一个步骤中更改编码,请这样做(尽管这种情况应该很少见!但是在你编写任何代码之前,请确保你确定你需要什么,否则你会陷入巨大的纠结。

不过,最终您将开始检测可以提供帮助的模式。例如,如果您期望一些美味的非 ASCII 输出,而是看到包含大量"Å">字符的奇怪文本,那么这可能是 UTF-8 被错误地解释为 ASCII。这是因为在 UTF-8 中表示大于一个字节的 Unicode 代码点的特殊序列通常以一个字节开头,该字节的数值与 ASCII 中字母"Å">的数值相同(嗯,ISO/IEC 8859,但足够接近)。

同样,如果你得到日语并且没有预料到它,根据我的经验,这通常是因为你给了计算机一些字节并告诉它它们是 UTF-16 编码的字符串,而实际上它们是 UTF-8。随着工作的增加,您在识别这些模式方面会更有经验,它可以帮助您更快地修复错误。

就在上周,最后一个示例为我节省了相当多的时间:我立即知道我的源数据一定是 UTF-8,因此能够快速决定将字节副本删除到我一直在尝试的std::wstring中。以与编码无关的方式检查字节也揭示了"Å">模式,然后就是这样。这很重要,因为我没有数据源的文档,因此无法仅查找编码应该是什么。我不得不猜测/推断它。希望这里不会是这种情况。

std::string只是一个8位宽char的容器,并且不知道/关心编码。始终用符号(字母、数字、标点符号等)思考前 128 个字符 (0-127) 是根据 ASCII 标准定义的,因此需要单个char来存储每个符号。对于所有的语言和符号,我们不能只用256种可能性来代表它们中的每一个。UTF-8 编码引入了一种处理此问题的方法,允许单个符号采用 1、2、3 或 4char宽。但是,对于std::string对象,这是完全透明的,它仍在处理一系列字符。

您认为字符串乱码的原因可能是因为调试器假定std::string的内容始终是每个字符 1 个符号(例如扩展 ASCII),因此,它显示的字符错误。

编辑:您可能还想阅读这篇文章。

最新更新