一、一个简单的 Qt程序
下面这个小程序,估计大家会感到比较亲切。似乎有相当多的中文用户尝试写过这样的代码:
#include <QtGui/QApplication>
#include <QtGui/QLabel>
int main(int argc, char **argv)
{
}
编码,保存,编译,运行,一切都很顺利,可是结果呢:
[list=disc]
多数用户看到 | 其他用户看到 |
ÎòêÇoo×Ö | 我æ˜ˉæ±‰å — |
出乎意料,界面上中文没显示出来,出现了不认识字符。
于是开始用搜索引擎搜索,开始上论坛发帖或抱怨
最后被告知,下面的语句之一可以解决问题:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
两条指令挨个一试,确实可以解决(
多数用户是第一条,其他用户是第二条)
。那么,为什么会这样呢?
二、两种乱码什么时候出现
对这个问题,我想大家可能都有话说。在继续之前,我们先列个表,看看两种乱码分别在那种情况下出现:
我们只列举大家最常用的3
个编译器(
微软VS
的中的
cl
,Mingw
中的g++
,Linux
下的
g++)
,源代码分别采用
GBK
和
不带BOM的UTF-8
以及
带BOM的UTF-8
这3
中编码进行保存。
[list=disc]
源代码的编码 | 编译器 | 结果 | |
GBK | cl | 1 | * |
mingw-g++ | 1 | * | |
g++ | 1 | ||
UTF-8(不带BOM) | cl | 2 | |
mingw-g++ | 2 | ||
g++ | 2 | * | |
UTF-8(带BOM) | cl | 1 | |
mingw-g++ | 2 | ||
g++ | 编译失败 |
采用3
种不同编码保存的源代码文件,分别用3
种不同的编译器编译,形成9
种组合,除掉一种不能工作的情况,两种乱码出现的情况各占一半。
从中我们也可以看出,乱码和操作系统原本是没有关系的。但我们在 Windows
一般用的GBK
,linux
一般用的是不带BOM
的UTF-8
。如果我们只考虑带
*
的情况,也可以说两种乱码和系统有关。
三、QString为什么会乱码呢
真的是 QString
乱码了吗?我们可以问问自己,我们抱怨的对象是不是搞错了?
继续之前,先明确几个概念:
明确概念0:
[list=disc]
- "我是汉字" 是C语言中的字符串,它是char型的窄字符串。上面的例子可写为
const char * str = "
我是汉字";
QString a= str;
或
char str[] = "
我是汉字";
QString a= str;
等
明确概念1:
[list=disc]
- 源文件是有编码的,但是这种纯文本文件却不会记录自己采用的编码
这个是问题的根源,不妨做个试验,将前面的源代码保存成GBK
编码,用16
进制编辑器能看到引号内是ce d2 ca c7 ba ba d7 d6
这样8
个字节。
现在将该文件拷贝到正体(
繁体)
中文的Windows
中,用记事本打开会什么样子呢?
...
...
那么放到欧美人的Windows
系统中,再用记事本打开呢?
...
...
同一个文件,未做任何修改,但其中的8
个字节ce d2 ca c7 ba ba d7 d6,
对用GBK
的大陆人,用BIG5
的港澳台同胞,以及用Latin-1
的欧洲人看来,看到的却是完全不同的文字。
明确概念2:
[list=disc]
- 如同我们都了解的'A'与'\x41'等价一样。
GBK
编码下的
const char * str = "
我是汉字"
等价于
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";
注意:这个说法不全对,比如保存成带BOM
的UTF-8
,用cl
编译器时,汉字本身是UTF-8
编码,但程序内保存时却是对应的GBK
编码。
明确概念3:
[list=disc]
- QString 内部采用的是Unicode。
QString
内部采用的是 Unicode
,它可以同时存放GBK
中的字符"
我是汉字",BIG5
中的字符"
扂岆犖趼"
以及Latin-1
中的字符"ÎòêÇoo×Ö"
。
一个问题是,源代码中的这8
个字节"\xce\xd2\xca\xc7\xba\xba\xd7\xd6"
,该怎么转换成Unicode
并存到 QString
内?按照GBK
、BIG5
、Latin-1
还是其他方式...
在你不告诉它的情况下,它默认选择了Latin-1
,于是8
个字符"ÎòêÇoo×Ö"
的unicode
码被存进了QString
中。最终,8
个Latin
字符出现在你期盼看到4
中文字符的地方,所谓的乱码出现了
四、QString工作方式
const char * str = "
我是汉字";
QString a= str;
其实很简单的一个问题,当你需要从窄字符串 char*
转成Unicode
的QString
字符串的,你需要告诉QString
你的这串char*
中究竟是什么编码?GBK
、BIG5
、Latin-1
理想情况就是:将char*
传给QString
时,同时告诉QString
自己的编码是什么:
就像下面的函数一样,QString
的成员函数知道按照何种编码来处理 C
字符串
QString QString::fromAscii ( const char *str, int size = -1 )
QString QString::fromLatin1 ( const char * str, int size = -1 )
QString QString::fromLocal8Bit ( const char * str, int size = -1 )
QString QString::fromUtf8 ( const char * str, int size = -1 )
单QString
只提供了这几个成员函数,远远满足不了大家的需求,比如,在简体中文Windows
下,local8Bit
是GBK
,可是有一个char
串是 BIG5
或 Latin-2
怎么办?
那就动用强大的QTextCodec
吧,首先QTextCodec
肯定知道自己所负责的编码的,然后你把一个char
串送给它,它就能正确将其转成Unicode
了。
QString QTextCodec::toUnicode ( const char* chars ) const
可是这个调用太麻烦了,我就想直接
QString a= str;
或
QString a(str);
这样用怎么办?
这样一来肯定没办法同时告诉 QString
你的str
是何种编码了,只能通过其他方式了。这也就是开头提到的
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GBK"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
设置QString
默认采用的编码。而究竟采用哪一个,一般来说就是源代码是GBK
,就用GBK
,源代码是UTF-8
就用UTF-8
。但有一个例外,如果你保存成了带BOM
的UTF-8
而且用的微软的cl
编译器,此时仍是GBK
。