String Unicode字符,C++;和libcurl

String Unicode字符,C++;和libcurl,string,unicode,encoding,stringstream,stdstring,String,Unicode,Encoding,Stringstream,Stdstring,我使用stringstream和libcurl下载数据。我也有一个解析函数 bool parse() { istringstream temp(buff.str()); buff.str(""); string line; QString line_QStr, lyrics_QStr; while (temp.good()) { getline(temp, line); if (QString::fromStdStr

我使用stringstream和libcurl下载数据。我也有一个解析函数

bool parse()
{
    istringstream temp(buff.str());
    buff.str("");
    string line;
    QString line_QStr, lyrics_QStr;
    while (temp.good())
    {
        getline(temp, line);
        if (QString::fromStdString(line).contains(startMarker)) break;
    }
    if (!temp.good()) return false; // something went wrong

    while (temp.good())
    {
        getline(temp, line);
        if ((line_QStr = QString::fromStdString(line)).contains(endMarker))
        {
            lyrics_QStr += line_QStr.remove(endMarker); // remove the </div>
            break;
        }
        else
        {
            lyrics_QStr += line_QStr;
        }
    }

    if (!temp.good()) return false;

    QTextDocument lyricsHtml;
    lyricsHtml.setHtml(lyrics_QStr);
    lyrics_qstr = lyricsHtml.toPlainText();
    return true;
}
编码仍然存在一些问题

EDIT2:我使用此函数将数据保存到stringstream中

size_t write_data_to_var(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    ostringstream * stream = (ostringstream*) userdata;
    size_t count = size * nmemb;
    stream->write(ptr, count);
    return count;
}
我将std::ostringstream buff传递给curl,网页数据保存在这里。 然后我使用一个wistringstream,将buff.str()转换为wstring,并将其用作wistringstream的源。
从std::string到std::wstring的转换就是解码,不是吗?

Web服务器返回一个字节流,该字节流与一个头一起,指示这些字节应该被理解为什么编码。如果调用QString::fromStdString时不考虑编码,那么默认情况下Qt将使用拉丁语1。在您的例子中,服务器发送UTF-8数据,并将其解析为Latin1,结果就是您给出的那种断开的文本

作为一种快速解决方法,您可以使用QTextCodec::setcodeforcstrings全局设置正确的编码。但这不是线程安全的

理想情况下,您应该在解析Web服务器返回的字节流之前对其进行解码,然后将其转换为带有fromStdWString的QString。根据经验,您希望尽早解码文本数据。请参阅Joel Spolsky关于如何处理Unicode的著名文章:

编辑:本质上,您在代码中缺少了一个步骤:获取服务器返回的字节流,并将其转换为正确的、无歧义的文本

您可能会发现将文本和字节流视为完全不同的动物很有用。核心区别在于文本是明确的:它是一个明确定义的字符串和字符标记(变音符号),以内在的方式存在,不受实现细节的约束。然而,字节流可能意味着什么,这取决于您如何解释它们

取字节0xC2 0xA3。它们的意思可能是“字符后跟字符”。这是一个完全正确的解释。但它们也可能意味着“角色”。这是另一个完全正确的解释

这些解释就是我们所说的编码。在第一种情况下,编码为Windows-1250,在第二种情况下,编码为UTF-8。请允许我在此重申,这两种编码都可能是正确的。也许发送这些字节的人真的想说Ł。也许是真的。也许这完全是另外一回事,在不知道编码的情况下,你无法说出那是什么

这里的想法是:一个你不知道其编码的字节流基本上是无用的

不幸的是,许多语言仍然允许您传递字节流并假装它们是文本。C++是不可免疫的:STD::string类型,尽管它的名字有误导性,实际上是字节流。不要让这个名字欺骗你

当你像传递文本一样传递字节时,最终负责显示该文本的子系统将对字节进行解码。(这是一条重要的经验法则:如果显示文本,那么字节在某处被解码。)只有上述子系统通常会使用默认编码(ASCII,拉丁语1),如果这不正确,那么,这就是为什么会出现意外字符

这里的核心问题是:您获取Web服务器发送给您的字节流,丢弃随它而来的编码信息,然后盲目地将字节传递给Qt

当您尝试从std::string构建QString时,Qt会尝试提供帮助,并假定一种常用的编码通常可以工作。我认为这不是一个好主意,因为这恰恰导致了你的问题;我认为如果QString需要显式编码,那么从长远来看会更好

因此,在此之前,您必须以不同的方式解决问题

谢天谢地,有一个已知的正确方法来处理这类问题

还记得我说的没有编码的字节流是没有意义的吗?Web服务器通常会向您发送一个编码,作为内容类型HTTP头的一部分。类似于
内容类型:text/html;字符集=iso-8859-1

字符集是您的编码:这里是iso-8859-1,这是拉丁语1的另一个名称

(注意:如果内容是HTML,则编码也可以在
http equiv
meta-header标记中给出。如果该标记与http头不一致,则假定http头是正确的。)

您希望立即使用该编码将这些字节转换为“实际”文本

在越来越多的语言中,“实际”文本是一种特定类型,不同于字节流。但是,C++中,你只能靠自己。 管理文本的标准方法是将其从初始编码转换为UTF-16,并将结果存储在std::wstring中。原因是UTF-16可以存储几乎任何文本而不会产生歧义。(如果改用UTF-32,您将能够存储任何文本,包括使用罕见的旧亚洲字符的文本,而存储成本为内存的两倍。)

老实说,我有点希望libcurl能为你做这件事;其他语言中的其他lib会返回正确解码的文本,而不是字节。但据我所知,这里没有这样的运气

但是!您不使用原始C++,您使用Qt,QT附带了用于正确文本处理的工具。 因此,您将尽可能早地将字节转换为QString,同时手头仍有编码,然后就可以了。qString是正确的文本,而不是字节流;字节流的Qt类型是QByteArray

那么,告诉你,让我们完全放弃wstring,只使用qstring

为了解决您的问题,以及您将遇到的任何编码问题,您必须:

1/找出预期的编码;在您的例子中,您将解析Content-Type头以确定编码。或者libcurl可以告诉你
wstring string2wstring(const string &str)
{
    wstring wstr(str.length(), L' ');
    copy(str.begin(), str.end(), wstr.begin());
    return wstr;
}
size_t write_data_to_var(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    ostringstream * stream = (ostringstream*) userdata;
    size_t count = size * nmemb;
    stream->write(ptr, count);
    return count;
}
QTextCodec *codec = QTextCodec::codecForName( figured_out_encoding );
QString string = codec->toUnicode( byte_stream );