windows下qt与C字符编码转换记录

文章主要讨论了在Windows环境下,使用Qt开发时遇到的日志文件和终端输出中文内容出现乱码的难题。作者通过分析发现是由于Qt与C/C++字符编码不一致导致的,并提供了设置编码以及转换函数的解决方案。同时,文章还涉及了与C/C++动态库交互时的数据编码问题。
摘要由CSDN通过智能技术生成

  在工作中使用qt,发现qt生成的日志文件和在终端输出的中文内容总是出现乱码。这已经影响了开发和排查问题的效率,于是决定研究一下。开发平台是在win10下,开发环境是qt5.15.2+vs2019。

  经过排查发现,程序中使用的日志库是公司底层模块同事用C/C++语言写的,也就是说每次qt程序写日志都是将日志内容传给C/C++语言程序再生成日志文件或者输出到终端,为什么不直接用qt去实现日志功能,我只能说是历史包袱。

  上网查了很多资料,知道是qt和C/C++字符编码格式的问题,于是写个demo进行测试,如下。

#if _MSC_VER >= 1600 //vs2010及其以上版本
#pragma execution_character_set("utf-8")  //设置执行字符编码
#endif

#include "mainwindow.h"
#include <QApplication>
#include <QTextCodec>
#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <QLibrary>

#ifdef Q_OS_WIN
  #include <windows.h>
#endif


/*
 *   windows下需要使用
 *   下面这连个转换函数也是从网上抄的,实际上实现的是多字节和宽字节互相转换的功能
 *   而中文对应的UTF8正好是多字节(3个字节表示),而中文对应的GBK正好是宽字节(固定的2个字节表示)
 */
#ifdef Q_OS_WIN

std::string UTF8ToGBK(const char* strUTF8)
{
    int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
    wchar_t* wszGBK = new wchar_t[len+1];
    memset(wszGBK, 0, len*2+2);
    MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
    len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
    char* szGBK = new char[len+1];
    memset(szGBK, 0, len+1);
    WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
    std::string strTemp(szGBK);
    if(wszGBK) delete[] wszGBK;
    if(szGBK) delete[] szGBK;
    return strTemp;
}

std::string GBKToUTF8(const char* strGBK)
{
    int len = MultiByteToWideChar(CP_ACP, 0, strGBK, -1, NULL, 0);
    wchar_t* wstr = new wchar_t[len+1];
    memset(wstr, 0, len+1);
    MultiByteToWideChar(CP_ACP, 0, strGBK, -1, wstr, len);
    len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
    char* str = new char[len+1];
    memset(str, 0, len+1);
    WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
    std::string strTemp = str;
    if(wstr) delete[] wstr;
    if(str) delete[] str;
    return strTemp;
}

#endif



int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();


    //设置中文编码
    #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))

        QTextCodec *codec = QTextCodec::codecForName("UTF-8");
        QTextCodec::setCodecForLocale(codec);
        QTextCodec::setCodecForCStrings(codec);
        QTextCodec::setCodecForTr(codec);

    #else

        QTextCodec *codec = QTextCodec::codecForName("UTF-8");   //设置本地字符编码
        QTextCodec::setCodecForLocale(codec);
        qDebug()<<"本地环境编码 = "<<codec->name();

    #endif


//中文编码16进制查询网站https://www.qqxiuzi.cn/bianma/zifuji.php

/*
    测试字符串输出中文是否正确,有很多转换的方式,如下,
    qDebug() << str;
    qDebug() << QStringLiteral("2中文");
    qDebug() << QString::fromLatin1("3中文");
    qDebug() << QString::fromLocal8Bit("4中文");
    qDebug() << QString::fromUtf8("5中文");
    qDebug() << QString::fromWCharArray(L"6中文");
*/


/*数据输出测试

  windwos下 :
                终端显示       qtide
                日志文件       将日志内容输出到txt文件中

  linux下 :
                终端显示       默认终端  qtide
                日志文件       将日志内容输出到文件中

*/


//C语言风格代码测试 char const * p
{
    char const * ch = " C 常量指针变量指向中文字符串,然后输出\n";

    //使用qt方式输出
    qDebug()<<"C Format Code "<<__LINE__<<" "<<ch;

#ifdef Q_OS_WIN
    //转码输出
    printf("C Format Code %d %s",__LINE__,UTF8ToGBK(ch).data());
    fflush(stdout); //使得printf按顺序输出与编码无关

    std::cout<<"C Format Code "<<__LINE__<<" "<<UTF8ToGBK(ch)<<std::endl;
#endif

    //不转码输出用作参照
    printf("C Format Code %d %s",__LINE__,ch);
    fflush(stdout);
    std::cout<<"C Format Code "<<__LINE__<<" "<<ch<<std::endl;


    //使用C/C++的方式写入文件
    FILE * fp;
#ifdef Q_OS_WIN
    //windwos下转码写入
    fp = fopen("./C/cwirteToGbk.txt","w");
    if(fp)
    {
        fwrite(UTF8ToGBK(ch).data(),1,strlen(UTF8ToGBK(ch).data()),fp);
    }
    fclose(fp);
#endif

    //不转码写入
    fp = fopen("./C/cwirte.txt","w");
    if(fp)
    {
        fwrite(ch,1,strlen(ch),fp);
    }
    fclose(fp);

}


//C++ 风格代码测试 string
{
    std::string cplusstr = " 中文\n";

    //qt方式输出
    qDebug()<<"C++ Format Code "<<__LINE__<<" "<<cplusstr.data();


    //C++方式输出
#ifdef Q_OS_WIN
    std::cout<<"C++ Format Code "<<__LINE__<<" "<<UTF8ToGBK(cplusstr.data());
    fflush(stdout);
#endif

    std::cout<<"C++ Format Code "<<__LINE__<<" "<<cplusstr.data();
    fflush(stdout);

    //C++方式写入文件
    std::ofstream ofs;
#ifdef Q_OS_WIN
    ofs.open("./CPLUSPLUS/cpluswirteToGbk.txt", std::ios::out);
    ofs << UTF8ToGBK(cplusstr.data());
    ofs.close();
#endif

    ofs.open("./CPLUSPLUS/cpluswirte.txt", std::ios::out);
    ofs << cplusstr;
    ofs.close();
}


//qt 风格代码测试 QString
{
    QString qtest = "  中文";
    qDebug()<<"qt Format Code "<<__LINE__<<qtest;

    //转换成不同的编码进行输出
    QByteArray array = qtest.toUtf8();
    qDebug()<<"qt Format Code "<<__LINE__<<array.constData();
    qDebug()<<"qt Format Code "<<__LINE__<<" array hex = "<<array.toHex();

    array = qtest.toLocal8Bit();
    qDebug()<<"qt Format Code "<<__LINE__<<array.constData();
    qDebug()<<"qt Format Code "<<__LINE__<<" array hex = "<<array.toHex();

    //qt方式写文件
    QFile file("./QT/qtwrite.txt");
    file.open(QIODevice::WriteOnly);
    file.write(qtest.toStdString().data(),qtest.toStdString().size());
    file.close();

    file.setFileName("./QT/qtwriteToUtf-8.txt");
    file.open(QIODevice::WriteOnly);
    file.write(qtest.toUtf8(),(qtest.toUtf8()).size());
    file.close();

    file.setFileName("./QT/qtwriteToGbk.txt");
    file.open(QIODevice::WriteOnly);
    file.write(qtest.toLocal8Bit(),(qtest.toLocal8Bit()).size());
    file.close();
}


/*
 *  与动态库交互数据
 *
 *  向C/C++库中传入数据和从C/C++中获取数据测试
 *  C和C++库在windwos下 V2019 编译
 */

//windows下的测试
#ifdef Q_OS_WIN
{
      //定义函数指针
      typedef std::string (*ptransdatastr)(std::string);    //输入string返回string
      typedef char * (*ptransdatachar)(char *);             //输入char * 返回char *


      QLibrary * qlib = new QLibrary();
      qlib->setFileName("./candcplusforwin.dll");
      if(qlib->load())
      {
          ptransdatastr ptransdatastrfun  = (ptransdatastr)(qlib->resolve("transdatastr"));
          if(ptransdatastrfun)
          {
                QString cplus = " 中文";
                std::string dllredata = ptransdatastrfun(UTF8ToGBK(cplus.toLocal8Bit().constData())).data();

                qDebug()<<"return from dll transdatastr : "<<__LINE__<<dllredata.data();

                qDebug()<<"return from dll transdatastr : "<<__LINE__<<GBKToUTF8(dllredata.data()).data();
          }
          else
          {
                qDebug()<<"解析符号transdatastr失败";
          }


          ptransdatachar ptransdatacharfun = (ptransdatachar)(qlib->resolve("transdatachar"));
          if(ptransdatacharfun)
          {
              QString cplus = " 中文";
              std::string dllredata = ptransdatacharfun((char *)UTF8ToGBK(cplus.toUtf8().constData()).data());

              qDebug()<<"return from dll transdatachar : "<<__LINE__<<dllredata.data();

              qDebug()<<"return from dll transdatachar : "<<__LINE__<<GBKToUTF8(dllredata.data()).data();
          }
          else
          {
              qDebug()<<"解析符号transdatachar失败";
          }
      }
      else
      {
          qDebug()<<"加载库candcplusforwin.dll失败";
      }
}
#endif

    return a.exec();
}



/*
    本地环境编码 =  "UTF-8"
    C Format Code  123    C 常量指针变量指向中文字符串,然后输出

    C++ Format Code  167    中文

    qt Format Code  196 "  中文"
    qt Format Code  200   中文
    qt Format Code  201  array hex =  "2020e4b8ade69687"
    qt Format Code  204   中文
    qt Format Code  205  array hex =  "2020e4b8ade69687"
    return from dll transdatastr :  250 ??????c++??????  ????
    return from dll transdatastr :  252 来自于c++库数据  中文
    return from dll transdatachar :  266 ??????c++??????  ????
    return from dll transdatachar :  268 来自于c++库数据  中文
    C Format Code 127  C 常量指针变量指向中文字符串,然后输出
    C Format Code 130  C 常量指针变量指向中文字符串,然后输出

    C Format Code 134  C 甯搁噺鎸囬拡鍙橀噺鎸囧悜涓枃瀛楃涓诧紝鐒跺悗杈撳嚭
    C Format Code 136  C 甯搁噺鎸囬拡鍙橀噺鎸囧悜涓枃瀛楃涓诧紝鐒跺悗杈撳嚭

    C++ Format Code 172  中文
    C++ Format Code 176  涓枃
    transdatastr  9   中文
    transdatastr 10   中文
    transdatachar  24   中文
    transdatachar 25   中文
*/

  下面是C/C++动态库的代码

string transdatastr(string str)
{
	cout << __FUNCTION__ << "  " << __LINE__ << "  " << str << endl;
	printf("%s %d  %s\n", __FUNCTION__, __LINE__,str.data());
	str = "来自于c++库数据 " + str;
	

	ofstream ofs;
	ofs.open("./windllwritestr.txt", ios::out);
	ofs << str;
	ofs.close();

	return  str;
}

char* transdatachar(char* ch)
{
	cout << __FUNCTION__ << "  " << __LINE__ << "  " << ch << endl;
	printf("%s %d  %s\n", __FUNCTION__, __LINE__, ch);
	string temp = ch;
	temp = "来自于c++库数据 " + temp;

	ofstream ofs;
	ofs.open("./windllwritech.txt", ios::out);
	ofs << ch;
	ofs.close();

	return (char *)temp.data();
}

  我认为日志内容选择UTF-8编码格式就挺好,毕竟兼容性强,windwos和linux上都可以使用。所以在代码中我将执行字符集和本地字符编码都设置成了UTF-8。

  在获取终端输出的时候,需要注意,我原本想使用debugview++输出上面的所有内容,结果发现C/C++输出的内容并没有显示,我只能在qt ide上的应用程序输出窗口显示,此时要注意这个窗口是以什么编码格式显示内容的,我这里设置的是GBK,所以下面代码中转码不会输出乱码,不转码输出乱码。

    #ifdef Q_OS_WIN
        //转码输出
        printf("C Format Code %d %s",__LINE__,UTF8ToGBK(ch).data());
        fflush(stdout); //使得printf按顺序输出与编码无关

        std::cout<<"C Format Code "<<__LINE__<<" "<<UTF8ToGBK(ch)<<std::endl;
    #endif

        //不转码输出用作参照
        printf("C Format Code %d %s",__LINE__,ch);
        fflush(stdout);
        std::cout<<"C Format Code "<<__LINE__<<" "<<ch<<std::endl;

  下图是设置窗口显示内容编码格式的位置

avatar



  希望能帮到读者,如果发现问题,请大家指教。

要将Qt中的十六进制字符转换为其他进制,可以使用Qt的QString类的toUInt()函数。该函数可以将字符转换为无符号整数,并且可以指定进制。例如,如果要将十六进制字符转换为十进制,可以使用以下代码: QString hexString = "A1B"; // 十六进制字符串 bool ok; int decimalValue = hexString.toUInt(&ok, 16); // 将十六进制字符转换为十进制 QString decimalString = QString::number(decimalValue); // 转换为十进制字符串 如果要将十六进制字符转换为二进制,可以使用以下代码: QString hexString = "A1B"; // 十六进制字符串 bool ok; int decimalValue = hexString.toUInt(&ok, 16); // 将十六进制字符转换为十进制 QString binaryString = QString::number(decimalValue, 2); // 转换为二进制字符串 如果要将十六进制字符转换为十进制或二进制,请使用上述代码的相应部分。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [qt中十进制转换为十六进制和二进制字符串,以及二进制字符串转十进制,十六进制字符串](https://blog.csdn.net/blqzj214817/article/details/120955923)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Qt十六进制字符串转十六进制编码](https://blog.csdn.net/qq_15094525/article/details/105741389)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值