转载:https://my.oschina.net/fanhuazi/blog/740594
本文假设你所使用的开发环境为Qt5.6+vs2015,为了能够复现乱码的问题我们先随便建立一个Qt的空工程,添加一个main.cpp文件(UTF8格式),使用qDebug(),和std::count输出一句汉语“我是中国人”:
#include <qdebug.h>
#include <iostream>
void main()
{
//使用Qt的qDebug()输出
qDebug()<<"qdebug:"<<"我是中国人";
//使用C++标准库输出
std::cout<<"std count:"<<"我是中国人"<<std::endl;
}
结果如下所示:
std count:我是中国人
qdebug: ?????й???
使用C++的标准输出(std::cout)输出了正确的结果,而使用Qt的qDebug()则没有得到正确的结果,仿佛Qt的“兼容性”不如C++似的,首先我们要搞懂字符串的编译过程:
(1)程序员在文本文件中书写字符串,事实上源文件中所有的东西都是字符串,需要“传输到”到程序中进行处理的是使用双引号括起来的“我是中国人”。此时“我是中国人”在文本文件中按照UTF-8格式存储。
(2)编译器(Linux为G++,vc为cl.exe)读取源文件的内容并进行词法分析,此时字符串的内容被转换为编译器的内部格式,有的朋友喜欢讨论VC具体使用什么编码,GCC什么编码,在此这些讨论时毫无意义的,因为编译器会按照厂家的商业需要随时改变其内部格式。此时,例子中使用的VC编译器在中国使用的编码为GBK,于是“我是中国人”从UTF-8格式转换为GBK格式存储在编译器的“缓存”里面。注意,如果编译器按照错误的编码格式读取源文件,那么任何字符串都将被解释为错误的乱码。常规的UTF-8编码通过添加BOM标记可以完美的解决这一问题。
(3)编译器将字符串的内容转换为目标文件中的全局变量,“我是中国人”这五个字在源码中形象的被称之为“存储在全局数据区的类型为const char *的字符串”,直到此时,这五个字才转变为“const char *”存储在全局数据区。这个时段,编译器(说的就是msvc)想当然地把GBK版本的“我是中国人”依旧存储为GBK格式。
(4)在运行期间,程序对字符串的格式完全处于无知的状态,显然 const char *是没有表露任何格式信息的。但是为什么std::cout却能够输出完全正确的结果呢?神探狄仁杰曾经说过,这世界上根本没有巧合,只有精心策划的阴谋:微软当然不会自己编写程序把自己的编译器搞出来的const char *输出乱码!呵呵!在Qt4的时代,用户可以在需要格式话加载字符串的场合之前调用以下代码来显式的指定const char *的编码:
QTextCodec::setTextCodcForTr(...)
想想也知道这又多麻烦,万一有几处代码没有设置,岂不麻烦。在Qt5的时期,Qt官方果断的删除了这一系列的函数,所有的转换const char *的动作均按照UTF-8处理。
根据以上的论述,要保证输出不乱码,总结需要保证四条:
(1)源文件格式清楚明白(作者自己必须心里有数)(2)编译器按照正确格式读取;(3)编译器生成全局const char *的格式要搞明白;(4)全局const char *的格式和Qt的字符串加载所需格式对上。
解决(1),(2)的内容,需要设置编辑器文件编码,在QtCreator的设置如下图所示,如果你的源文件不是UTF-8建议批量转成UTF-8:
由于Qt已经明确要求全局数据去的字符串必须是UTF-8格式了,那么就必须通过编译器来设置,在使用VC的情况下,加入这些代码就成生效,前提是用的VS是2012以上的版本(相信大家都已经用新的了):
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
即便不是天才也能看出来这个做法比设置QTextCodec还要麻烦,难道每个文件都要添家三句代码吗?好在Qt的工程管理中已经支持将一个头文件预编译,可以通过这种机制让工程中所有的源文件全部在编译时段自动的包含同一个头文件。
新建一个文件,设名称为utf8.h,将pragma命令语句添加里面,然后在Qt的工程文件中添加:
#设置编码统一使用utf8
PRECOMPILED_HEADER += $$GTDIR/include/utf8.h
将整个代码工程重新qmake,重新编译,乱码一去不复返,且对代码无污染。
讲了上面那么多理论,我们用刚才的小工程测试一下,main函数变更为:
#include <qdebug.h>
#include <iostream>
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
void main()
{
//使用Qt的qDebug()输出
qDebug()<<"qdebug:"<<"我是中国人";
//使用C++标准库输出
std::cout<<"std count:"<<"我是中国人"<<std::endl;
}
输出结果为下图所示:
std count:鎴戞槸涓浗浜
qdebug: 我是中国人
qDebug好了,std::cout确乱了,没办法,谁叫他们用的格式不一样呢,当然在这里,Qt是大局。相信你不会用std::cout再输出东西的。
如果非要使用stdout输东西,一定记得转换为本地格式,不要计较本地格式是啥,QString的toLocal8bit会帮你做判断的,因此,将代码改为:
#include <qdebug.h>
#include <iostream>
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
void main()
{
//使用Qt的qDebug()输出
qDebug()<<"qdebug:"<<"我是中国人";
//使用C++标准库输出
std::cout<<"std count:"<<QString("我是中国人").toLocal8Bit().data()<<std::endl;
}
我们得到了完全正确的输出:
std count:我是中国人
qdebug: 我是中国人