Qt开发中文显示乱码

Qt开发中文显示乱码

来源 https://www.jianshu.com/p/ed269df8104d

参考 https://blog.csdn.net/J_H_C/article/details/93882284

为什么会出现乱码

首先,我们需要有的概念是乱码的问题是由编码和解码方式引起的。涉及到编码方式的地方有3个:

  1. 源码字符集
  2. 执行字符集
  3. 运行环境字符集

源码字符集确切的说是编译器认为源码文件的编码方式,执行字符集是可执行程序采用的编码方式,而运行环境字符集则是环境支持的编码方式。编译程序处理字符串的过程,实际上是首先读入字符的二进制数,根据编码格式到另一种编码格式转换策略得到另外一串二进制数,所以1->2可能有二进制数的变化,而3则是通过既定的编码方式来解读2中的二进制数为字符(这里为什么说可能呢,因为1和2如果是相同的编码是不需要变化的)。

那么具体是哪些地方引起错误呢?在解答之前先介绍理解该问题的先验知识(由于我的运行环境是window简体中文版,所以以下的locale编码就是指GBK编码):

  • msvc2013编译程序时,处理源码字符集时,有BOM标识符的则正确识别(实际上目前就是有无BOM的utf-8),无BOM则使用本地Locale字符集(随系统设置而变),执行字符集默认用本地Locale字符集(其他msvc版本在看完本文甚至可以根据自己实验猜测处理)。
  • gcc编译程序时,默认两者都是uft-8,有finput-charset源码字符集和fexec-charset执行字符集则按照设置。

那么乱码的原因有:
①编译器解读源码字符集错误。如我是utf-8的源码,因为不带bom你当成locale,执行字符集也是locale所以不需要转换,而本来utf-8到locale是需要转换的。
②源码字符集到执行字符集的转换错误。如本来把识别正确的源码字符集locale转成执行字符集中的utf-8,结果你给我指定了错误了转换方式,说让我通过xxx编码转utf-8的策略转(Note:这是错误的表述,看到下面你就明白,实际上这里的错误只能是应为转换算法的错误)。
③字符解析错误。如果现在程序中的字符串二进制是utf-8的,结果你非要说执行字符集是loacle,那么解析肯定会出错。

还需要理解的包括下面的知识:

  • windows console控制台代码页为locale,即把程序中的字符串二进制表示当locale执行字符集来解读
  • 字符串二进制的表示形式不需要编译,直接拷贝到执行程序的二进制中

乱码情况解析

接下来内容的实例基于csdn作者“在水一方”博文中举的“我是中文”的例子(文末有引用),他的博文在我理解这个问题的本质过程中帮助很大。这里就套用他的例子的,一方面我比较懒,不想举其他例子,另一方面通过验证他的例子,也佐证了我自己的想法。
直接上例子(这里说的都是源码字符集):

const char * str = "我是汉字"
//用GBK编码等价于
const char * str = "\xce\xd2\xca\xc7\xba\xba\xd7\xd6";
//用utf-8编码等价于
const char * str = "\xe6\x88\x91\xe6\x98\xaf\xe6\xb1\x89\xe5\xad\x97";
//note:这里的等价于就是说当计算机到内存中来处理的时候,中文读入的就是等价于的二进制数

翻译一下就是,“我是汉字”这几个字,在GBK编码下就是保存的“\xce\xd2\xca\xc7\xba\xba\xd7\xd6”这样一串二进制,而utf-8则是保存的“\xe6\x88\x91\xe6\x98\xaf\xe6\xb1\x89\xe5\xad\x97”。这里可以使用Notepad++进行验证。

字符解析错误乱码
  • 编译环境:vs2013(msvc2013编译器),源码文件字符集GBK
  • 运行环境:Windows简体中文下的Console命令行

下面看一段代码:

char * cc = "\xce\xd2\xca\xc7\xba\xba\xd7\xd6";
std::cout << cc << std::endl;
char * cc1 = "\xe6\x88\x91\xe6\x98\xaf\xe6\xb1\x89\xe5\xad\x97";
std::cout << cc1 << std::endl;
char * cc2 = "我是汉字";
std::cout << cc2 << std::endl;

运行程序得到下图结果:

根据结果我们可以看到2是乱码的,而汉字表现出了和GBK下二进制数据一样的结果。有了前面的先验知识按照前面先验的乱码原因①②③来理解:
①对于不带bom源码的文件,msvc2013当成locale处理,而源码字符集恰巧是locale,读入源码字符集没问题。这里需要“我是汉字”字符串变为二进制数,并记录源码字符集。
②源码字符集和执行字符集都是locale,不需要转换,没转换自然转换没问题。到此,字符串的二进制表示的直接拷贝到了执行程序中。
③2把执行程序中“\xe6\x88\x91\xe6\x98\xaf\xe6\xb1\x89\xe5\xad\x97”——“我是中文”uft-8编码下的二进制,当成了GBK编码来解析,所以出现了类型③乱码。
Note:请用notepad++检验,以便理解。

在上面程序的基础上,我们添加测试函数的函数体前添加一段预定义,这是c++11对执行字符集的支持:

//让编译器编译生成程序的执行字符集为utf-8
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif

再次运行程序,得到如下的结果:

首先看到12和上面结果一样,有人这里就有疑问了。你说的字符串的二进制表示直接拷贝我也理解,但是现在我的执行字符集是utf-8啊,那我解读第一个和第二个的结果不应该是这个啊。那你可能忘掉了我之前的一个先验知识了,console不认识utf-8,它仍然会把这串二进制当成locale来解读,所以这里和上面的表现结果是一样的。
下面来看3是怎么回事,①②流程下来:
①源码为locale,编译器也默认认为源码字符集是locale(编译器这是瞎猫碰到死耗子,蒙对了!),解读正确。
②编译器正确知道源码字符集的情况下,需要转化成指定的字符集,自然是会给出正确的转化策略。
最终,编译通过编码转换策略做了一次从 “\xce\xd2\xca\xc7\xba\xba\xd7\xd6”到“\xe6\x88\x91\xe6\x98\xaf\xe6\xb1\x89\xe5\xad\x97”的转换,所以程序中又是“我是中文”uft-8编码下的二进制了,最终又回到了2的情况——类型③乱码。

转换错误乱码(反证)
  • 编译环境:QtCreator(MinGW gcc编译器),源码文件字符集utf-8
  • 运行环境:Windows简体中文下图形界面

下面看一段代码:

MainWindow w;
QLabel *lb1 = new QLabel(&w);
QLabel *lb2 = new QLabel(&w);
QLabel *lb3 = new QLabel(&w);
lb1->setText(QString("我是汉字"));
lb1->resize(100, 20);
lb1->move(120, 120);
lb2->setText(QString::fromUtf8("我是汉字"));
lb2->resize(100, 20);
lb2->move(120,160);
//由于源码是utf-8编码,下面代码等价于:
//lb3->setText(QString::fromLocal8Bit("我是汉字"));
lb3->setText(QString::fromLocal8Bit(
              "\xe6\x88\x91\xe6\x98\xaf\xe6\xb1\x89\xe5\xad\x97"));
lb3->resize(100, 20);
lb3->move(120,200);
w.show();

运行程序得到下图结果:

这里我不给出详细的分析了,通过第1个标签和第2个标签结果都正常,可以验证出gcc编译的默认规则——默认源码字符集和执行字符集都是uft-8,且知道了Qt中QString::fromxxx()函数的作用了。而标签2和标签3的对比可以知道,当环节②出错,就出现乱码了。过程是编译器把读入的utf-8编码下的二进制当成了loacle来解析,这时就解析成了所谓的那串“乱码”,然后正确转换成了uft-8编码下的该“乱码”(Note:这里两次乱码实际的二进制是不一样的哦,只是编码形式不同才有的相同结果,你明白我的意思嘛?)。有人又要疑惑了,不对啊,你明明说这是个类型②的错误,怎么我看着像是类型①乱码呢。其实如果你能这么疑惑,说明你是真的懂了,这里确实是一个类型①的乱码。实际上这里的源码字符集到执行字符集的算法是api内部实现的,所以我们面对这种情况的时候②都不会出问题的。当然了,像你这种乱码都没有理解的人来说,去实现这个算法,那我是不敢用,说不定就会产生类型②乱码了,哈哈。

总结

由于Qt的出现就是为了跨平台,所以QString中统一采用utf-16存储字符串。所有源码中的字符串存放到QString中时,都需要经过一次到utf-16的正确转换。在qt5之前,有两种解决方式解决乱码:

QString::fromxxx();
QTextCodec::setCodecForxxx();

相信大家看了前面已经明白这两个函数是意思,这里要提醒一句的就是,两种方式最终在QString中存放的,都是字符串在unicode编码形式下的二进制。

写在最后

这系列的文章将会以自己学习后理解的知识点分享为主,希望吾之所得亦可为汝所得。在2017年3月18日重新更新文章时,我删掉了与知识点无关的表述。只是希望让正努力从“不求甚解”到“先去理解清楚一些以释重负”转变的你,不会因为篇幅过长望而却步。如有疑问,欢迎提问,如有高见,烦请指点。
参考:

-----------------------

来源  http://blog.csdn.net/aqtata/article/details/9184741

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

网上很多人一碰到编码问题就无脑的Copy上面3行……
从Qt5开始只剩下setCodecForLocale这一个了,只是影响Qt对toLocal8Bit相关函数的编码方式

{
    // Qt默认会使用本机编码,所以对于中文系统,下面这句设置是多余的
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));  

    QString str1("你好Hello");
    QByteArray bLocal = str1.toLocal8Bit(); // 受setCodecForLocale影响,会转换为设定的编码。如果本机不支持指定编码,则会按toLatin1处理
    QByteArray baLatin1 = str1.toLatin1();  // 不受setCodecForLocale影响,强制转换为ISO-8859-1编码
    QByteArray bUtf8 = str1.toUtf8();       // 不受setCodecForLocale影响,强制转换为UTF-8编码
    qDebug() << str1;                       // 正常,Qt会将UTF-16转换为UTF-8输出
    qDebug() << baLatin1;                   // 乱码,用UTF-8编码输出Latin1字节流
    qDebug() << bLocal;                     // 乱码,用UTF-8编码输出GBK字节流
    qDebug() << bUtf8;                      // 正常,用UTF-8编码输出UTF-8字节流  

    QString str2 = QString::fromLocal8Bit(bLocal);
    qDebug() << str2;                       // 正常,因为上面显式指定字节流来自本机编码,而bLocal正是本机编码GBK  

    str2 = QString::fromLatin1(bLocal);
    qDebug() << str2;                       // 乱码,bLocal是GBK编码,但却指定了以Latin1方式去读取,肯定会乱码  

    // 字节流来自UTF-8
    str2 = QString::fromUtf8("\xE4\xBD\xA0\xE5\xA5\xBD\x48\x65\x6C\x6C\x6F");
    qDebug() << str2;                       // 正常  

    // Qt默认采用UTF-8处理字符串,所以不用显式地去调用fromUtf8
    str2 = QString("\xE4\xBD\xA0\xE5\xA5\xBD\x48\x65\x6C\x6C\x6F");
    qDebug() << str2;                       // 正常  

}

在Qt中,QString会用UTF-16编码存储,而qDebug()等I/O函数会以UTF-8编码处理。

其实转换后的字节流是正确的,只是显示时用了和字节流不同的编码方式处理导致乱码

所以当要在Qt中输入输出非UTF-8字符串时应该先转换一下

在源码中要写入非英文字符的话建议使用转义的方式,也就是上面“\xE4\xBD...”这种,这也是官方推荐的方式

为了方便将字符串转换为UTF8转义字符,写了一个小工具

http://download.csdn.net/detail/aqtata/5596247

2014-5-6补充:

从vs2010sp1和vs2013开始就已经支持UTF-8的源码文件了,只用在工程里加入一句"#pragma execution_character_set("UTF-8")"即可。不用再做上面的转义了。

============= End

原文地址:https://www.cnblogs.com/lsgxeva/p/12161743.html

时间: 2024-10-13 17:57:26

Qt开发中文显示乱码的相关文章

pycharm开发python时出现控制中文显示乱码

当pycharm开发python时,中文出现乱码 修改setting文件 这样就能正常显示 pycharm开发python时出现控制中文显示乱码

解决osgEarth中文显示乱码的几种方法

解决osgEarth中文显示乱码的几种方法 在此感谢那些在路上那个帮助过别人的朋友,谢谢. 方法一: 通过自己写函数转换类型. 下面这三个函数先复制过去吧. void unicodeToUTF8(const std::wstring &src, std::string& result) { int n = WideCharToMultiByte( CP_UTF8, 0, src.c_str(), -1, 0, 0, 0, 0 ); result.resize(n); ::WideCharT

将中文库导入到ARM板子中以解决中文显示乱码的教程

1.将中文字符集导入到ARM板子中的/usr/fonts/目录下 在这里我们使用的字符集为:DroidSansFallback.ttf 下载地址为:https://pan.baidu.com/s/1ARn2Y6Vh6MsRhjHP18yMwQ 中文字符集具体的导入过程如下: (1)将字符集DroidSansFallback.ttf拷贝到SD卡中. (2)在板子的/usr目录下新建文件夹fonts,同时提高文件夹的权限. 命令如下: mkdir fonts chmod 777 fonts 命令 (

putty中文显示乱码解决方法

putty工具是开源免费的远程ssh工具,界面整洁,小巧好用,配合同样小巧整洁且免费的WinSCP的ftp功能,绝对是linux后端开发的利器 说下解决putty中文显示乱码的问题 1.更改linux系统的语言环境 vim ~/.bash_profile 在最后添加一行   LANG="zh_CN.UTF-8" :wq 退出保存 最后运行 source ~/.bash_profile 2.修改putty显示 首先ssh连接到你修改过~/.bash_profile 的服务器,登陆后,右键

Atom中文显示乱码问题 UTF-8

Atom中文显示乱码问题 本来就是UTF-8文件,但中文还是显示不了.换了GBK.GB18030也不行. 解决方法: 给atom编辑器设置字体 从菜单中打开 Edit->Open your config选项,或者Packages->Setting views->Open, 找到config.cson文件中的editor子项,或者setting views中的font-family选项,把字体设置成 文泉驿 系列字体才可以显示中文.比如: 文泉驿微米黑,文泉驛微米黑,WenQuanYi M

Linux中文显示乱码?如何设置centos显示中文

Linux中文显示乱码?如何设置centos显示中文 怎么设置Linux系统中文语言,这是很多小伙伴在开始使用Linux的时候,都会遇到一个问题,就是终端输入命令回显的时候中文显示乱码.出现这个情况一般是由于没有安装中文语言包,或者设置的默认语言有问题导致的.以centos为例,操作Linux怎么查看语言,设置修改语言 1.查看当前系统语言 登陆linux系统打开操作终端之后,输入 echo $LANG可以查看当前使用的系统语言.如 2.查看安装的语言包 查看是否有中文语言包可以在终端输入 lo

ubuntu 中用vim打开txt等文件中文显示乱码的解决方法 &nbsp;

今晚用ubuntu 12.04 的vim编辑网页代码时,发现上面的中文显示乱码.这个html文件是我之前用gedit编辑的,在gedit里面显示正常,可是在vim下显示就是乱码了. 怎么解决ubuntu中vim显示中文乱码的问题呢? 首先,添加中文字符编码: sudo gedit /var/lib/locales/supported.d/local 在打开的文件中添加下面的中文字符集 zh_CN.GBK GBK zh_CN.GB2312 GB2312 zh_CN.GB18030 GB18030

解决Sublime Text 3中文显示乱码问题(转)

解决Sublime Text 3中文显示乱码问题(转) 1.打开Sublime Text 3,按Ctrl+-打开控制行,复制粘贴以下python代码,然后回车运行. 2. 复制并粘贴如下代码: import urllib.request,os,hashlib; h = '7183a2d3e96f11eeadd761d777e62404e330c659d4bb41d3bdf022e94cab3cd0'; pf = 'Package Control.sublime-package'; ipp = s

安装redhat时中文显示乱码(小方框)解决方法

在安装linux的时候,安装完了中文出现乱码或者是当时选错了选成了英文的,到时候中文显示乱码,下面说一下问题的解决: 在首次安装RHEL5时,如果选择的是英文,那么系统将不安装中文支持包,这样就导致了中文显示为乱码(小方框)....... 有很多人说vi /etc/sysconfig/i18n文件,其实根本就没有那个必要. 解决方法: 安装 1.fonts-chinese-3.02-9.6.el5.noarch.rpm.  如果无法安装,则加个--force 2.fonts-ISO8859-2-