把字符转换成计算机可以识别的比特流的方法就叫做字符编码。
显然,美国人的ASCII码是不能表示中国文字的,因为它只能表示256个字符。中国的标准像GB2312,GBK,GB18030 虽然可以表示美国人的文字,但是却不能表示阿拉伯字符和其它一些象型文字。人们意识到这是文化交流的障碍,于是就制定了Unicode标准。简言之,Unicode标准就是穷尽这个世界上所有的文字,每个字符对应一个编码。其实Unicode和GB2312,GBK等一样,也只是一种编码标准,只不过它能够表示所有的文字。从此,如果一个计算机系统想要支持多种语言文字,只要简单地支持Unicode就可以了。
虽然解决了编码不能相互表示的大问题,但是还有一个麻烦没有解决:所谓编码就是给文字编一个数字。在每个编码里,数字与文字是一一对应的,但是在不同的编码标准可能包含相同的文字,却被编了不同的数字。于是同一个文字对应了不同的数字。另外同一个数字是否代表着同一个文字呢?显然不是的,不然,两种编码标准就只有包含文字多少的区别了,或者每个文字在两个编码对应了相同的数字,那这还是两个编码标准吗?
如今这个世界上以前的标准仍然盛行,特别是Windows仍然使用旧的标准。在中文简体的Windows上,用记事本写的文本文件仍然是GBK编码的。想要在一个使用Unicode的浏览器上显示出文字来依然不行,不同的编码环境从文件里读取的编码不会变,但是却表示了不同的文字。怎么办呢?很容易想到的办法是读取文件内容的时候把GBK编码转成Unicode编码。因为GBK包含的文字只是Unicode包含的文字的一部分而已,从GBK转成Unicode应该是没问题的。反之,从Unicode转成GBK却不一定可以。
另外一种情况是两种编码标准差不多,只是每个文字在两种编码标准里对应的编码不一样。比如GBK和CJK,两种编码标准都包含了大量的中文简体文字和繁体文字,但是两种编码是不一样的。一个GBK的文本文件想拿到只支持CJK的环境里阅读也要在读取文件的时候从GBK编码转成CJK编码。目前一般采用先把GBK转成Unicode,然后再从Unicode转成CJK的间接做法。在上一种情况下我们已经知道:GBK转成Unicode是可以的,但是Unicode的文字转成GBK却不一定可以。但是因为如果真的不能从Unicode转到CJK的话,说明那个文字CJK根本不能表示,直接从GBK转成CJK肯定也不可以。相对于直接从GBK转成CJK的方式而言,间接的转码多了一个步骤,不过形式上比较统一,只要每种编码都编写一段与Unicode相互转换的程序,就可以进行任意编码的相互转换。
此外再有就是虽然编码是一样的,但是编码转换成字节组的方式也是可以有多种方式的;最典型的是Unicode编码。Unicode编入了很多个字符,而且个数总是在增加,把文字串转换成Unicode字节组最简单的办法就是每个字符用三个字节来表示,因为Unicode目前为止大约有100713个字符,这个数字最少需要三个字节才能表示。实际上通常采用的是UTF-8方案。其中,最常使用的128个英文字母用一个字节来表示,中文使用三个字节来表示。还可以使用UTF-16方案,其中英文和中文都使用两个字节来表示,而其它字符采用四个字节。还有一种UTF-32方案,所有的文字都用四个字节来表示。大多数软件在内存中使用UTF-16来表示文字,而在硬盘文件或者网络数据流中使用UTF-8。前者字节数比较固定,比较容易处理,而后者比较节约空间。
现在说明一下Qt对于字符编码的处理。
Qt为字节流和字符串分别提供了QByteArray和QString两个类(还有QLatin1String等其他类,但这两个是最主要的)。当我们涉及到I/O时,比如读写文件、读写网络socket、控制台输入输出、读写串口... 操作的都是字节流,如果我们此时需要操作的内容是字符串,则需要二者之间的相互转换。在C和C++中,我们一般都是将 "Hello World!" 这种称为字符串。但是就目前而言,当我们提字符串时,一般是指一个Unicode字符串,其由一个一个的Unicode字符构成;当我们提字节流时,是指一个一个的字节。或许我们可以说,ANSI C/C++截止目前只有字节流,而缺乏对字符串的支持。另外各个编译器对编码的支持又严重不一, Qt为解决这个问题提供了QTextCodec。
一旦在Qt程序中出现latin1字符集以外的字符,几乎大家无一例外的会用到 QTextCodec。甚至有不少网友不分青红皂白,一旦用到中文,就同时使用下面3条指令:
QTextCodec * textc = QTextCodec::codecForName("GBK");
1.QTextCodec::setCodecForCStrings(textc);
2.QTextCodec::setCodecForTr(textc);
3.QTextCodec::setCodecForLocale(textc);
这3条指令简单来说就是为了实现字符串与字节流之间的转换(也就是字符的编解码)。
QTextCodec * textc = QTextCodec::codecForName("GBK");
1.QTextCodec::setCodecForCStrings(textc);
2.QTextCodec::setCodecForTr(textc);
3.QTextCodec::setCodecForLocale(textc);
这3条指令简单来说就是为了实现字符串与字节流之间的转换(也就是字符的编解码)。
QString 是不存在中文支持问题的,很多人遇到问题,并不是本身 QString 的问题,而是没有将自己希望的字符串正确赋给QString。很简单的问题,"我是中文"这样写的时候,它是传统的char 类型的窄字符串,我们需要的只不过是通过某种方式告诉QString 这四个汉字采用的那种编码。而问题一般都出在很多用户对自己当前的编码没太多概念。另外文件是有编码的,但是这种纯文本文件却不会记录自己采用的编码,这个是问题的根源。真的是 QString 乱码了吗?其实很简单的一个问题,当你从窄字符串 char* 转成Unicode的QString字符串的时候,你需要告诉QString你的这串char* 中究竟用的是什么编码?GBK、BIG5还是Latin-1。理想情况就是:将char* 传给QString时,同时告诉QString自己的编码是什么;但是QString 提供的成员函数,远远满足不了大家的需求,于是只有采取语句1的办法。
tr是用来实现国际化的,前提是你为这个程序提供了翻译包,但是他需要经过多级函数调用才实现了翻译操作,是有代价的,所以不该用的时候最好不要用。相比QCoreApplication:: translate,tr大家应该用的更多一些。tr(“中文”);与QString("中文");一样,你必须告诉tr这个窄字符串是何种编码?你不告诉它,它就用latin1。于是所谓的乱码问题就出来了。如何告诉tr你写的这几个汉字在磁盘中保存的是何种编码呢?这正是语句2所做的。如果你的编码采用的utf8,可以直接使用trUtf8而不必设置setCodecForTr()。
对于语句3应该没什么好说的,在绝大多数情况下,我们在代码中应该都用不到这个函数(默认的system应该比我们所能设置的要好)。
PS:一个使用QTextCodec类的qt程序,在具有qt开发环境下的windows上运行很正常,而在没有qt开发环境下的windows上运行,在运行到QTextCodec对象时,就会弹出遇到错误。程序里注消了QTextCodec对象代码,程序运行正常,但不能处理中文了。
Qt中带了很多插件(Plugin),在Qt目录下的"qt\plugins"下有一个"codecs"的文件夹。下面的文件从文件名就可以区别出来是“简体中文,繁体中文,日文,韩文”的编码插件。将该文件夹拷贝到程序所在的目录,程序运行就正常了(当然,codecs文件下面的".a"文件都可以删除,".dll"文件也可以把带"d"的debug版本的删掉。注意的是一定要把codecs文件夹放在发布程序的同目录下,且不要更改目录名字。)。总之除了Qt运行的Core和Gui等库,使用QTextCodec类后,程序打包的时候别忘了带上plugins\codecs。
对一些特殊的处理还要注意是不是带了相应的插件。这很重要!