Qt对字符的处理(beta1.0)

来源:互联网 发布:文件夹隐藏软件 编辑:程序博客网 时间:2024/05/17 01:54

为什么字符在Qt中显示的是乱码?gemfield本文以《带你到离汉字库最近的地方》这篇文章为基础,再详究文章开始处提出的疑问。本文中代码实现的环境如下:windows xp sp3+ Qt 4.6.2 以及 GNU/Linux-ubuntu-10.04+ Qt 4.6.2。

本文的后继文章《Qt的翻译系统》将在接下来的1个月内问世,因此本文对翻译部分不做解释。
先来说说一个现象:为什么在designer里的控件上(比如Qlabel)写入的汉字可以正常显示,而在程序代码里写入的汉字呈现出来的却是乱码?这是因为前者已经悄然借助了Qt的翻译系统——Qt 在将ui文件moc为ui_*.h文件时,使用了QApplication::translate()方法。这里面的详情请参考gemfield的《Qt的翻译系统》,这里就不做介绍了。
好了,下面来开始我们的探究之旅。先从最简单的开始(在windows xp sp3+ Qt 4.6.2上编译通过):

****************************************************************
unsigned char gemfield[25]=”青岛之光 civilnet.cn”;
for(int i=0;i<25;i++)
qDebug()<<hex<<(quint8)gemfield;
QString gemfield_1(“青岛之光 civilnet.cn”);
qDebug()<<gemfield_1;
qDebug()<<”青岛之光 civilnet.cn”;
—————————————————————————————————–

程序输出:
Starting D:\gemfieldCareer\gemfield\international\debug\international.exe…
c7
e0
b5
ba
d6
ae
b9
e2
20
63
69
76
69
6c
6e
65
74
2e
63
6e
0
0
0
0
0
“?àμo??1a civilnet.cn”
?àμo??1a civilnet.cn
*************************************************************
我们看到“青岛之光”这几个汉字成为了乱码,为什么会是乱码呢?因为你在windows下键入汉字的时候,使用的是gbk编码。也就是说,这些汉字是以gbk编码被存入可执行程序的。当你把生成的 .exe文件用记事本打开时,“青岛之光”这几个汉字能正常显示,因为记事本程序默认使用的是ANSI编码,ANSI在中国大陆反映出来就是gbk编码。所以你以gbk编码规则存入,再以gbk编码规则读出,当然是不会有问题的。那么Qt里执行出来为什么是乱码?因为Qt默认使用的是unicode解码,也就是说,汉字以gbk编译到二进制文件里,却以unicode规则解码,当然是乱码了。
再往下详究,gbk是以2个字节存储一个汉字的,上面代码中字符数组输出的前8项对应的就是“青岛之光”这4个汉字的编码。分别是c7 e0 b5 ba d6 ae b9 e2,这个可以到字符映射表里查找。这时你会问,linux下的情况呢?以ubuntu10.04为例,字符默认是使用UTF8编码的,也就是以UTF8编码规则将“青岛之光”存入二进制文件,程序执行的时候则以unicode来解码。所以Qt程序运行的时候也显示的是乱码,但是乱码会和这里的不一样。UTF8是以3个字节存储一个汉字的,所以程序的输出中,应该是3个字节代表1个汉字,而不是上面的2个字节代表一个汉字。
知道问题的原因了,那怎么解决呢?请看下面的代码(在windows xp sp3+ Qt 4.6.2上编译通过):
****************************************************************
QTextCodec::setCodecForCStrings(QTextCodec::codecForName(“GBK”));
unsigned char gemfield[25]=”青岛之光 civilnet.cn”;
for(int i=0;i<25;i++)
qDebug()<<hex<<(quint8)gemfield;
QString gemfield_1(“青岛之光 civilnet.cn”);
qDebug()<<gemfield_1;
qDebug()<<”青岛之光 civilnet.cn”;
——————————————————————————————————————–

程序输出:
Starting D:\gemfieldCareer\gemfield\international\debug\international.exe…
c7
e0
b5
ba
d6
ae
b9
e2
20
63
69
76
69
6c
6e
65
74
2e
63
6e
0
0
0
0
0
“青岛之光 civilnet.cn”
青岛之光 civilnet.cn
*************************************************************
可以看出,在添加了QTextCodec::
setCodecForCStrings(QTextCodec::codecForName(“GBK”));这句代码后,汉字的输出正常了。因为它设置了在字符从c7 e0 b5 ba d6 ae b9 e2这种QByteArrays
类型转换为“青岛之光”这种char类型时所依据的解码规则。这里明确指定了gbk,因此汉字的输出就正常了。前提是你的系统要支持gbk。而在ubuntu10.04上,系统默认是utf8编码,所以此处代码应该为QTextCodec::setCodecForCStrings(QTextCodec::codecForName(“UTF8″));以UTF8编码,再以UTF8解码,汉字就不会乱码了。
掌握了上面gemfield描述的本质的话,我们再看一个例子来验证一下。看下面的代码(在windows xp sp3+ Qt 4.6.2上编译通过)::
***********************************************************************************************
QString gemfield_1(“青岛之光 civilnet.cn”);
qDebug()<<gemfield_1;
***********************************************************************************************
你认为输出是什么呢?乱码,肯定的,以gbk编码再以unicode解码(Qt默认),当然是乱码。Gemfield稍微改造一下程序:
(在windows xp sp3+ Qt 4.6.2上编译通过):
***********************************************************************************************
QTextCodec *gemfield_codec = QTextCodec::codecForName(“gbk”);//当前系统为gbk编码
QString gemfield_1=gemfield_codec->toUnicode(“青岛之光 civilnet.cn”);//将字符转换为unicode编码
qDebug()<<gemfield_1;
***********************************************************************************************
这次完整的输出中文了:青岛之光 civilnet.cn,明白为什么了吗?虽然我们没有使用QTextCodec::setCodecForCStrings(QTextCodec::codecForName(“GBK”));来改变解码方式,但是我们使用了
“toUnicode(“青岛之光 civilnet.cn”);”将这几个汉字转换为unicode码存放到gemfield_1变量里了。而Qt默认是使用unicode解码的,unicode存储unicode解码,当然不会是乱码了。
类似于这种toUnicode()、fromUnicode()、fromLocal8Bit()、toLocal8Bit () 方法都是当事方和unicode之间的转换,比如最后一个就是local编码和unicode之间的转换。gemfield再使用 fromLocal8Bit()举个例子(在windows xp sp3+ Qt 4.6.2上编译通过):
***********************************************************************************************
QString gemfield_1;
qDebug()<<gemfield_1.fromLocal8Bit(“青岛之光 civilnet.cn”);
***********************************************************************************************
输出什么呢?是乱码吗(因为没有使用QTextCodec::setCodecForCStrings(QTextCodec::codecForName(“GBK”));)?答案是,输入规范的汉字。

为什么呢?因为fromLocal8Bit()将当前的参数转换为unicode(上面刚说过),而前面也说过了Qt是使用unicode解码的,unicode存储unicode解码,当然不会是乱码了。(此句第二次出现了,呵呵)。

关于代码中输出文字的介绍就先到这儿,接下来gemfield分析一下读取文件时的编码。
打开windows下的记事本或者ubuntu下的gedit,输入以下文字:“青岛之光 civilnet.cn”,并保存为gemfield.txt。然后编写代码  ,代码如下
(在windows xp sp3+ Qt 4.6.2上编译通过):
***********************************************************************
int main(int argc,char *argv[])
{
QApplication gem(argc,argv);
QFile gemfield_file(“gemfield.txt”);
if(!gemfield_file.open(QIODevice::ReadWrite))
{
qDebug()<<”cannot open the file “<<endl;
}
QTextStream out(&gemfield_file);//读取文本文件,使用QTextStream
QString gemfield_c,gemfield_d;//声明2个变量
out>>gemfield_c>>gemfield_d;//文本文件gemfield.txt的内容放入这两个变量,以空格区分
gemfield_file.close();
qDebug()<<gemfield_c<<gemfield_d;//相当于输出文本文件里的内容
return gem.exec();
}
***********************************************************************
我们假设现在是在windows xp下进行这些操作,你认为输出是什么呢?经过gemfield本文开始部分的讲解,你一定会想到,我在记事本里写入那些字符最后保存时用的是ANSI编码(默认的,在中 国大陆地区就反映为gbk),而代码里又没有使用QTextCodec::setCodecForCStrings(QTextCodec::codecForName(“GBK”));所以输出肯定是乱码!
gemfield说你这个想法不错,不过这里有所不同。因为读取文本文件时我们使用了
QTextStream类,这个类可是相当了不起的,虽然QTextStream的缓冲区是基于unicode编码的,但是在读取文件时 ,QTextStream类会自动调用codec = QtextCodec::codecForLocale()方法,所以它已经得知你在windows xp下是用gbk编码了,所以使用setCodec()方法后就不会是乱码了。因此上面的程序并不会输出
乱码。
但是如果你把windows xp下的记事本文件拿到ubuntu下做个试验,你会发现它是乱码,因为ubuntu下的local不同于windows xp,它支持的是utf8。(你在ubuntu下使用gedit编写gemfield.txt文本文件,然后再在ubuntu下运行上面的代码,就会正常显示。)那怎么修改呢?

(在GNU/linux-ubuntu-10.04+ Qt 4.6.2上编译通过):
***********************************************************************
int main(int argc,char *argv[])
{
QApplication gem(argc,argv);
QFile gemfield_file(“gemfield.txt”);
if(!gemfield_file.open(QIODevice::ReadWrite))
{
qDebug()<<”cannot open the file “<<endl;
}
QTextStream out(&gemfield_file);
out.setCodec(QTextCodec::codecForName(“gbk”));//设置读写时所用的编码
QString gemfield_c,gemfield_d;
out>>gemfield_c>>gemfield_d;
gemfield_file.close();
qDebug()<<gemfield_c<<gemfield_d;
return gem.exec();
}
**************************************************************************
这样windows xp下的记事本文件就可以在ubuntu下正常读取了。当然前提是ubuntu要提供对gbk文件编码的支持,刚安装的ubuntu是不支持gbk的。关于这一点,CivilNet的论坛已经有足够的 资料来帮助你。同理,在ubuntu下用gedit编辑的gemfield.txt在windows xp下用上面的程序读取也会显示乱码,因为gedit默认的是utf8编码,所以此时应该使用out.setCodec(QTextCodec::codecForName(“utf8″));了。
此外,QTextStream还会自动检测unicode下面的UTF16和UTF32编码以自动使用正确的解码规则读取文件。
好了,关于Qt对字符的处理就先思考到这儿,再往下深入的话边际成本就会猛增,这是gemfield所不能承受的。然而,本篇文章的题目依然使用了”beta1.0″的字样,就表明gemfield愿意在以后的思考中以及其他人提供的反馈后不断的修正文中的错误,继续增加必要的知识。
再次说明,本篇文章以《带你到离汉字库最近的地方》为基础,本篇文章的后续文章是《Qt的翻译系统》。
备注:本篇文章属于gemfield博客(www.civilnet.cn/gemfield)“Qt乐园”版块,bug提交至gemfield@civilnet.cn,转载此文时,请保证包括<备注>在内的文章的完整性。

此条目由 gemfield 发表在 Qt乐园 分类目录,并贴了 Qt、QTextCodec、TextStream、unicode、编码 标签。将固定链接加入收藏夹。