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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

  在工作中使用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



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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: windows