如何将utf8转换为std :: string?

时间:2019-03-11 00:27:53

标签: c++ unicode utf-16 cjk cpprest-sdk

我正在处理此代码,该代码接收到一个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

}

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

2 个答案:

答案 0 :(得分:2)

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

存储

这是我们保存/存储/保存数据的方式。 std::stringchar的集合,它们是 bytes 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将引入std::u8_string,其意图是表示UTF-8编码的字符串(否则或多或少类似于std::string)。

但这只是惯例。类型std::string上没有任何内容表示“ UTF-8”或其他任何内容。它不了解,不关心或不执行任何编码。它只是存储字节。

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

“那我该怎么办?”

好吧,Base64也不是编码。嗯,实际上,它完全是,但是它是在字符串编码之上的 编码。这是一种传输/转义/清除原始字节的方法,而不是描述以后如何解释它们的方法。通过asking cpprest to convert from 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,这与您要描述的描述相反。

(类似地,在第二个示例中,您使用了a std::wstring that [probably] already stores UTF-16 thanks to the L"wide string literal",然后告诉计算机它是UTF-8,并将其“再次”转换为UTF-16,然后将原始字节提取为{ {1}}。这都没有道理。)

相反,为什么不从字面上看只是std::string

一般建议

编码可能非常复杂,但是一旦您将所有这些抽象层的基本概念都放在脑海中,处理起来就会非常容易。对于未来,以及对于这个问题,如果您想澄清一下,您将需要确保在数据“管道”从阶段A传输到地点B的每个阶段都绝对清楚从类型C转换为类型D,以及其他方式,说明在每个步骤中其应采用的编码方式。如果您想在其中一个步骤中更改编码,则可以这样做(尽管这种情况很少见!)。但是在编写任何代码之前,请确保已确定所需的内容,否则您将陷入困境。

最终,您将开始发现可以提供帮助的模式。例如,如果您期望获得一些美味的非ASCII输出,而看到的奇怪文本中包含许多“Å” 字符,则可能是UTF-8,它被错误地解释为ASCII。这是因为这样的方式,即表示UTF-8中大于一个字节的Unicode代码点的特殊序列通常以其数值与ASCII中的字母“Å” 相同的字节开头( ,ISO / IEC 8859,但足够接近)。

同样,如果您会日语,并且没想到,根据我的经验,通常是因为您给了计算机一些字节并告诉它它们是UTF-16编码的字符串,而实际上却是UTF- 8。您将在工作时发现这些模式,从而获得更多经验,并且可以帮助您更快地修复错误。

就在上周,那里的最后一个示例为我节省了很多时间:我立即知道我的源数据必须是UTF-8,因此能够迅速决定将字节副本删除为{{1 }}我一直在尝试。以与编码无关的方式检查字节也显示了“Å” 模式,然后就是那样。这很重要,因为我没有有关数据源的文档,因此也没有办法仅查找应设的编码。我不得不猜测/推断。希望这里不会对您如此。

答案 1 :(得分:0)

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

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

编辑:您可能还想阅读this post