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

问题描述 投票:2回答:2

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

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

}

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

c++ unicode utf-16 cjk cpprest-sdk
2个回答
2
投票

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

存储

这是我们保存/存储/保存数据的方式。 std::stringchars的集合,它们是字节。 std::wstringwchar_ts的集合,有时是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,然后将原始字节提取到std::string。这些都没有意义。 )

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

一般建议

编码可能非常复杂,尽管一旦你将所有这些抽象层的基本概念都包含在内,它就会更容易处理。对于未来,对于这个问题,如果你想澄清它,你将需要确保你在数据的“管道”的每个阶段都绝对清楚,因为它从A地传到B地,并得到从类型C转换为类型D,以及其他任何内容,关于在每个步骤中它应该是什么编码。如果你想在其中一个步骤中更改编码,那么这样做(尽管这应该是罕见的!)。但在你编写任何代码之前,请确保你确切知道你需要什么,否则你将陷入巨大的纠结。

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

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

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


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

© www.soinside.com 2019 - 2024. All rights reserved.