2023-08-25 C++使用libcurl库返回数据为乱码的原因分析与解决方案

C++使用libcurl库返回数据为乱码的原因分析与解决方案:


广告时间: 我新上了一门C语言课程 <C 语言编程核心突破> , 有入门需求的同学可以看看.


前言:

最近看到一个问题: C++使用libcurl库返回数据出现中文乱码, 这个应该是很常见的, 但题主的描述是错误的, 并非中文乱码, 而是全部乱码.

尝试解决一下, 毕竟libcurl如此经典, 基本做网络的都离不开, 但相关文档貌似没有明确的提供相关问题的解决方案, 或者说没有中文的解决方案.


问题描述

利用 libcurl 进行C++开发, 访问网页一般可正确返回内容, 比如:

www.baidu.com

, 访问一般的XML也可以正常的返回, 比如:

https://www.w3schools.com/xml/simple.xml

但是有些就比较诡异, 比如:

https://comment.bilibili.com/1057102166.xml

返回的内容完全是乱码, 连英文都是乱码.


��o]G�7��~�D�:]���Ձ?8A� �8I� �ױ05=3߼��)�"e�JQ�%�f�(Y��ŋ�O��k����/U�H����g��<�`�M�U}���UuU����?��ܟz_s��W����'s��:��'�����������'��OO~�i��ַ����S����Ǭ}��s��O����o��~0��_��~��'�F��O����O�l��/N~y��ϼ�_�ӧ�|���'�f��ӯ{/�ϯZ_�����oN���N����?}�O�ϟv���>>39'�?B��?D�sœ�B��`o|>1|�'��66��?M~*6V�����woF�gG�O󗯊�[��[�����v~�ѧ��-y��0XcNx���1;L�1FcO�̽���m!�g�


原因分析:

通常第一时间我们会想到可能是GBK和utf-8编码的问题, 但这个并不影响英文内容, 所以不成立.

排除了编码问题, 还有什么问题能引起乱码呢?

我能想到的就是源文件出了问题, 排除方法也很简单, 直接用浏览器打开看看, 结果是正常的:

<i>
<chatserver>chat.bilibili.com</chatserver>
<chatid>1057102166</chatid>
<mission>0</mission>
<maxlimit>500</maxlimit>
<state>0</state>
<real_name>0</real_name>
<source>k-v</source>
<d p="95.01700,1,25,16777215,1679053310,0,79c7792f,1274750575577579008,10">真正驯服了尾巴的猫就是这么强大</d>
<d p="42.47200,5,25,15138834,1679049902,0,88e7f73f,1274721985825661184,10">不敢相信1000个人在听猫叫</d>
<d p="56.18500,1,25,16777215,1679048871,0,a577d0c0,1274713336441193216,10">希望下次可以唱雾都雾里七个八</d>
<d p="78.41200,5,25,15138834,1679050479,0,a39d4f40,1274726828300470784,10">这人直接融化了是吧(doge)</d>
<d p="19.67000,5,25,15138834,1679047971,0,de508db1,1274705786559662592,10">主唱大人!</d>
<d p="117.80700,5,18,15138834,1679047962,0,f10a42f8,1274705711632589568,10">为什么这时嘴里没飞进去小虫子doge</d>
<d p="87.90900,5,25,15138834,1679047934,0,d2c8cbb2,1274705480946020608,10">鼓手摸鱼(doge)</d>
<d p="28.68000,5,25,15138834,1679048869,0,66945478,1274713325569447168,10">邻居:很难想象配音者的精神状态</d>
<d p="110.07700,1,25,16777215,1679049083,0,4534326,1274715118617495552,10">比内鱼大部分偶像唱得好(狗头)</d>
<i/>

这就说不通了, 没理由浏览器可用而libcurl的C++程序不可用, 于是用curl命令行进程测试, 结果是:

E:\clangC++>curl https://comment.bilibili.com/1057102166.xml                       
Warning: Binary output can mess up your terminal. Use "--output -" to tell 
Warning: curl to output it to your terminal anyway, or consider "--output  
Warning: <FILE>" to save to a file.

返回的是二进制文件, 为了看看究竟返回的是什么, 我用管道将其导入文件:

E:\clangC++>curl https://comment.bilibili.com/1057102166.xml > E:\clangC++\test.txt
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed  
100 24059    0 24059    0     0  41325      0 --:--:-- --:--:-- --:--:-- 41409

结果是:

��o]G�7��~�D�:]���Ձ?8A� �8I� �ױ0�5�=3߼��)�"e�JQ�%�f�(Y��ŋ�O��k����/U�H����g��<`�M�U}���UuU����?��ܟz_s��W����'s��:��'�����������'��OO~�i��ַ����S����Ǭ}��s��O����o��~�0��_��~��'�F��O����O�l��/N~y��ϼ�_�ӧ�|���'�f��ӯ{�/�ϯZ_�����oN���N����?}�O�ϟv���>�>3�9'�?B��?D�sœ�B��`o|>�1|�'��66��?M~�*6V�����woF�gG�O󗯊�[��[�����v~�ѧ��-y��0XcNx���1;L�1FcO�̽���m!�g�

依然是乱码.

回想为什么是二进制文件, 什么情况下可能是二进制文件?

数据压缩, 这是最大可能的方案, 因为浏览器可以正常查看, 说明文件内容一定是可被解析的, 但curl命令行返回的数据不能正常解析, 是二进制, 那么基本可以断定就是压缩的问题.


解决方案:

先用命令行进行验证, 我们看看curl的帮助:

E:\clangC++>curl -h
Usage: curl [options...] <url>
 -d, --data <data>          HTTP POST data
 -f, --fail                 Fail fast with no output on HTTP errors
 -h, --help <category>      Get help for commands
 -i, --include              Include protocol response headers in the output
 -o, --output <file>        Write to file instead of stdout
 -O, --remote-name          Write output to a file named as the remote file
 -s, --silent               Silent mode
 -T, --upload-file <file>   Transfer local FILE to destination
 -u, --user <user:password> Server user and password
 -A, --user-agent <name>    Send User-Agent <name> to server
 -v, --verbose              Make the operation more talkative
 -V, --version              Show version number and quit

This is not the full help, this menu is stripped into categories.
Use "--help category" to get an overview of all categories.
For all options use the manual or "--help all".

这里并没有压缩选项, 那我们再查看详细文档, 我找到了这一项:

   --compressed         Request compressed response

那么用命令行试一下:

E:\clangC++>curl --compressed https://comment.bilibili.com/1057102166.xml          
curl: option --compressed: the installed libcurl version doesn't support this
curl: try 'curl --help' for more information

没有这个功能支持, 看看我的版本:

E:\clangC++>curl -V    
curl 8.0.1 (Windows) libcurl/8.0.1 Schannel WinIDN
Release-Date: 2023-03-20
Protocols: dict file ftp ftps http https imap imaps pop3 pop3s smtp smtps telnet tftp
Features: AsynchDNS HSTS HTTPS-proxy IDN IPv6 Kerberos Largefile NTLM SPNEGO SSL SSPI threadsafe Unicode UnixSockets

8.0.1版本应该挺高了, 还是不行, 那么升级, 通过msys2进行curl库安装, 这个就不详细介绍了

$ pacman -S mingw-w64-clang-x86_64-curl

注意一下, 这个库是有依赖的, 所以需要安装相应的依赖

$ pacman -S mingw-w64-clang-x86_64-libssh2

然后测试:

E:\clangC++>E:\msys64\clang64\bin\curl --compressed https://comment.bilibili.com/1057102166.xml 
<?xml version="1.0" encoding="UTF-8"?><i><chatserver>chat.bilibili.com</chatserver><chatid>1057102166</chatid>
<mission>0</mission>
<maxlimit>500</maxlimit>
<state>0</state>
<real_name>0</real_name>
<source>k-v</source>
<d p="95.01700,1,25,16777215,1679053310,0,79c7792f,1274750575577579008,10">真正驯服了尾巴的猫就是这么强大</d>
<d p="42.47200,5,25,15138834,1679049902,0,88e7f73f,1274721985825661184,10">不敢相信1000个人在听猫叫</d>
<d p="56.18500,1,25,16777215,1679048871,0,a577d0c0,1274713336441193216,10">希望下次可以唱雾都雾里七个八</d>
<d p="78.41200,5,25,15138834,1679050479,0,a39d4f40,1274726828300470784,10">这人直接融化了是吧(doge)</d>
<d p="19.67000,5,25,15138834,1679047971,0,de508db1,1274705786559662592,10">主唱大人!</d>
<d p="117.80700,5,18,15138834,1679047962,0,f10a42f8,1274705711632589568,10">为什么这时嘴里没飞进去小虫子doge</d>
<d p="87.90900,5,25,15138834,1679047934,0,d2c8cbb2,1274705480946020608,10">鼓手摸鱼(doge)</d>
<d p="28.68000,5,25,15138834,1679048869,0,66945478,1274713325569447168,10">邻居:很难想象配音者的精神状态</d>
<d p="110.07700,1,25,16777215,1679049083,0,4534326,1274715118617495552,10">比内鱼大部分偶像唱得好(狗头)</d>
<d p="119.89400,4,25,15138834,1679070255,0,eacc5927,1274892716781634304,10">*听完这首歌使你充满了决心</d>

OK, 看来问题已经接近解决了, 那么如何实现代码呢?

继续查文档: CURLOPT_ACCEPT_ENCODING;

大致内容:

CURLOPT_ACCEPT_ENCODING explained
Name
CURLOPT_ACCEPT_ENCODING - automatic decompression of HTTP downloads
Synopsis
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ACCEPT_ENCODING, char *enc);

也就是需要在调取网页内容前加一句

curl_easy_setopt(CURL *handle, CURLOPT_ACCEPT_ENCODING, char *enc);

处理后的代码:

#include <curl/curl.h>
#include <fstream>
#include <iostream>
#include <string>

// 数据处理回调函数
auto writeCallback(char *contents, size_t size, size_t nmemb,
                   std::string *response) -> size_t
{
    size_t totalSize = size * nmemb;
    response->append(contents, totalSize);
    return totalSize;
}

auto main() -> int
{
    // 初始化
    curl_global_init(CURL_GLOBAL_DEFAULT);

    // 创建句柄
    CURL *curlHandler = curl_easy_init();

    if (curlHandler != nullptr)
    {
        // 设置SSL验证, 可以不验证, 但不推荐
        // curl_easy_setopt(curlHandler, CURLOPT_SSL_VERIFYHOST, 0L);
        // curl_easy_setopt(curlHandler, CURLOPT_SSL_VERIFYPEER, 0L);

        // SSL验证, 需要给出cacert.pem的路径
        curl_easy_setopt(curlHandler, CURLOPT_SSL_VERIFYHOST, 2L);
        curl_easy_setopt(curlHandler, CURLOPT_SSL_VERIFYPEER, 1L);
        curl_easy_setopt(curlHandler, CURLOPT_CAINFO,
                         "answer\\C++\\cacert.pem");

        // 设置要请求的URL
        curl_easy_setopt(curlHandler, CURLOPT_URL,
                         "https://comment.bilibili.com/1057102166.xml");
        // "https://www.w3schools.com/xml/simple.xml");
        // curl_easy_setopt(curlHandler, CURLOPT_URL, "https://www.baidu.com");

        // 设置内容解压
        curl_easy_setopt(curlHandler, CURLOPT_ACCEPT_ENCODING, "");

        // 设置回调函数,用于处理获取到的网页数据
        std::string response;
        curl_easy_setopt(curlHandler, CURLOPT_WRITEFUNCTION, writeCallback);
        curl_easy_setopt(curlHandler, CURLOPT_WRITEDATA, &response);

        // 发起请求
        CURLcode res = curl_easy_perform(curlHandler);

        if (res != CURLE_OK)
        {
            std::cerr << "请求失败,错误代码: " << res << std::endl;
        }
        else
        {
            // 内容输出到文件
            std::ofstream file("answer\\C++\\test271.txt");
            file.write(response.c_str(),
                       static_cast<long long>(response.size()));
            file.close();
        }

        // 清理curl句柄
        curl_easy_cleanup(curlHandler);
    }

    // 清理全局curl资源
    curl_global_cleanup();

    return 0;
}

验证成功.

总结:

C++对于字符编码和各种解码, 需要程序员付出极大的耐心, 在CSDN上的C/C++问答, 近乎每周都有乱码问题, 然而我在回答了众多问题, 觉得可以解决近乎所有乱码代码的情况下, 仍然会有本文这种新情况出现.

所以, 永远不要有一劳永逸的想法, 每解决一个问题, 都会产生一个新的问题, 这才是程序员的宿命.


广告时间: 我新上了一门C语言课程 <C 语言编程核心突破> , 有入门需求的同学可以看看.


C++使用libcurl可以方便地进行网络通信,包括发送HTTP请求、下载文件等操作。下面是使用libcurl的一般步骤: 1. 引入头文件:在代码中引入libcurl的头文件,通常是`#include <curl/curl.h>`。 2. 初始化和清理:在使用libcurl之前,需要调用`curl_global_init()`函数进行初始化,并在使用完毕后调用`curl_global_cleanup()`函数进行清理。 3. 创建和配置CURL对象:使用`curl_easy_init()`函数创建一个CURL对象,并使用`curl_easy_setopt()`函数设置相关选项,如设置请求的URL、设置请求的方法、设置请求头等。 4. 执行请求:使用`curl_easy_perform()`函数执行请求,该函数会阻塞直到请求完成。 5. 处理响应:可以通过设置回调函数来处理响应数据,如保存到文件、打印到控制台等。 6. 清理资源:使用`curl_easy_cleanup()`函数释放CURL对象。 下面是一个简单的示例代码,演示了如何使用libcurl发送GET请求并将响应打印到控制台: ```cpp #include <iostream> #include <curl/curl.h> // 回调函数,处理响应数据 size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* response) { size_t totalSize = size * nmemb; response->append((char*)contents, totalSize); return totalSize; } int main() { CURL* curl; CURLcode res; std::string response; // 初始化 curl_global_init(CURL_GLOBAL_DEFAULT); // 创建CURL对象 curl = curl_easy_init(); if (curl) { // 设置请求的URL curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); // 设置回调函数 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); // 执行请求 res = curl_easy_perform(curl); if (res != CURLE_OK) { std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; } // 打印响应数据 std::cout << response << std::endl; // 清理资源 curl_easy_cleanup(curl); } // 清理 curl_global_cleanup(); return 0; } ``` 这只是一个简单的示例,libcurl还提供了更多的功能和选项,可以根据具体需求进行配置和使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值