Qt入门教程(3) Qt的中文乱码原理讲解

  • A+
所属分类:Qt专栏 Qt系列教程

计算机眼中的字符串

计算机只能存储二进制位。8个二进制位是1个字节,无论是文本文件还是音乐文件,或者是二进制文件,对于计算机来讲,其实都是一连串二进制位,从某种角度讲,也把这个序列叫做字节流。我们使用的字符串,在计算机看来,其实就是一个字节流,即一连串的二进制位。想要让计算机理解我们人类世界的字符串,自然要创建字符映射为字节的编码规则,每一套规则对应着一套字符集,经过了长时间的演变,衍生出了许多种类的编码规则,也就产生了很多字符集。

字符串映射为字节流的过程叫编码;字节流映射为字符串的过程叫解码,只有编码和解码过程使用的字符集相同,才不会出现乱码问题。

Qt入门教程(3) Qt的中文乱码原理讲解

编码的前世今生

编码和解码过程需要定义映射规则,也就是我们熟知的各种各样的编码方式,如GBK、UTF-8等。

1、【01编码】:很久很久以前,为了表示二极管的通、分,我们引入了高电平、低电平,之后又引了的1、0编码进行代替。01编码是最原始的计算机语言编码方式,人类无法直接读懂。

2、【ASCII编码】:为了使得人类的语言和01编码相对应,很久以前,也就是上个世纪60年代,美国佬为了把计算机的“0101010”编码与文字进行对应起来,制定了一套ASCII编码方案。人总是自私的,他只对自己的语言进行编码,26个字母、数字、其他符号,只用了7位二进制数搞定,第一位用0表示,预留着。所以ASCII编码最多127编码

3、【“二代”ASCII编码】:不单单是美国佬想把计算机语言和英文联系起来,其他国家也想把自己的语言关联起来,法文、俄文就基于ASCII编码,将上一代ASCII第一位(未使用)变成1,来对自己国家的语言进行编码。

结果,每一个国家都基于一代ASCII,让第一位变成1,完成对本国的语言的编码。由于他们各干各的,没有沟通,从而导致了同一种编码出现不同的文字。

4、【gb2312编码】:我们伟大的祖国80年代也开始对汉字进行编码,由于我们的文字较多,即使是基于ASCII将第一位变成1,也不够我们使用(国语博大精深拉),所以就制定了一套gb2312编码,使用2个字节表示。

5、【BIG5编码】:我国的港澳地区,他们是使用繁体字(gb2312编码最初并没有考虑到繁体字),怎么办了?他们就出了自己的区域编码BIG5

6、【中国一统GBK编码】:为了统一汉字,迫切的需要设计出一种既能支持简体字又能表示繁体的新编码方案,GBK诞生了!他兼容了绝大部分gb2312编码(gb2312编码的文字用gbk可以读出来,但是不兼容BIG5编码)

7、【世界大统unicode编码】:各国编码各做各的,总不是意见好事,为了便于交流,国际社会引入了unicode——(uni统一的意思,code编码)把所有国家的文字都进行了编码。统一是一件好事,但是也是有问题的。英文只要1个字节ASCII编码就可以表示,你unicode还需要2个字节或更多,导致unicode表示英文的时候前面有很多无用的000000000000000000。

8、【解决unicode问题,UTF编码】:解决方法是使用utf8编码,它是基于unicode编码上的一种优化,英文使用1个字节,中文使用2、3个(绝大部分是3个,个别有看到2、4个的一般不用)字节。好处:我能屈能伸,我可以变化长度来保存,就不会浪费空间了吧。

Qt入门教程(3) Qt的中文乱码原理讲解

举个例子,如“我是汉字”分别用不用的编码格式表示为:

GBK: ce d2 ca c7 ba ba d7 d6

UTF-8 无BOM: e6 88 91 e6 98 af e6 b1 89 e5 ad 97

人类世界中的字符串,采用了不同的编码格式,导致最终的字节流也不一致。

代码编译过程中的编码转换

Qt入门教程(3) Qt的中文乱码原理讲解

所谓的乱码问题,其实仅仅是针对代码中的字符串部分,代码不会乱码,因此,上面的过程也仅仅针对字符串进行讲解

1、编码保存:这个过程其实大家都很熟悉,只是可能不知道为什么要这么做。一般,我们会在工程中设置编码方式。如在Qt Creator中设置界面为

Qt入门教程(3) Qt的中文乱码原理讲解

这里设置的编码其实就是源文件的编码方式,也就决定了字符串最原始的编码方式,图中的字符集A其实就是这里设置的“文件编码”对应的字符集。

2、读取源文件:其实在电脑程序看来,所有的文件都是字节码,程序根本不会将文件认为是一个个字符,因此,如果我们想弄明白程序是怎么进行编码转换的话,也要跟着电脑程序的思维走,将文件看成是一个个的字节码。

3、编码转换: Qt出发点为国际化项目,因此特别注重国际语言的编码。为了兼顾计算性能和存储性能,Qt决定将UTF-16作为QString的编码格式,至于其他如tr我还不了解,总之,只要是Qt自带的字符串类型,都会有固定的编码格式,以方便后续的字符串计算及显示。既然Qt的字符串具备固定的编码格式,但从源文件中读取的字节流却不一定和该编码格式相同,那么,必然要做编码转换。这过程中,程序员必须提供源文件中的字节流的编码方式,才能进行转换。虽说文件是按照A字符集映射编码的,但是,计算机读文件的时候只读到了字节码,根本不知道该字节码是通过哪种编码方式编码的,因此,也就不知道该怎么去把字节码解码成中文。那么,就会出现两种方式来解决这种问题。第一种是为编译器指定了默认的编码,只要程序没有特殊说明,就按照默认编码方式将字节码进行转换;第二种是对中文字符串的编码方式进行特殊说明。Qt中的字符串采用这三个函数进行特殊说明:

QTextCodec::setCodecForTr(codec);

//决定Tr(str)中str的编码格式

QTextCodec::setCodecForLocale(codec);

//1、决定与外部文件读写的时候使用的默认编码

//2、决定向命令行输出信息(qDebug)使用的编码

QTextCodec::setCodecForCStrings(codec);

//决定QString ( const char * str )和QString ( const QByteArray & ba )中str和ba使用的编码

其中codec即代表源文件中的字节流编码方式。转换完成后,Qt运行内部的字符串将全部统一为Qt规定的编码格式。

4、运行:程序运行时,如果不涉及读取外部文件路径等功能,则只要按照程序内部的编码方式正常运行即可。但是,如果涉及到和外部系统的交流,就要与外部系统默认编码进行转换,可通过QTextCodec::setCodecForLocale(codec);进行设置。

中文乱码的原因

之所以产生乱码,是因为解码和编码所采用的字符集不一样

Qt入门教程(3) Qt的中文乱码原理讲解

我们采用字符集A映射规则将人类所能理解的字符串转换为了计算机能理解的字节流,让计算机来帮我们处理复杂的计算事务,处理完毕后,计算机也需要将结果转换成为人类能够理解的字符串(不然全是二进制数字,谁能看懂)。这种编码和解码的过程中采用的字符集必须一致,才能保证转换不发生错误。如果解码过程采用了字符集B,那么就会发生乱码,典型的案例就是我们经常看到的“烫烫烫烫”。

举个例子,如你用GBK保存了中文字符串,也就是说,你用GBK的字符集去将字符串映射为字节流传递给了计算机,同时,你要告诉计算机,这个字节流是通过GBK编码的。计算机计算完成后,便会调用GBK字符集对字节流解码成字符串显示给人类。但是,如果你用GBK了编码中文字符串,却告诉计算机这是用UTF-8编码的,那么计算机接收的字节流实际上是GBK编码的字节流,但输出的时候,计算机却会调用UTF-8字符集去解码,最终就会乱码。

在程序保存、编译、运行过程中,不仅仅会多次发生字符串的编码和解码,同时,还可能存在编码规则的转换,这些过程中,一旦出现了某个字符集的使用失误,便可能导致中文出现乱码。

Qt代码示例

QString内部默认采用Unicode编码,所以,我们在使用QString时,都需要将QString中的字符串转换为Unicode。

QTextCodec *codec =QTextCodec::codecForName("UTF-8");

QString str = codec->toUnicode(“中文”);

这句话的意思是:

“中文”采用UTF-8方式编码,告诉QString要调用UTF-8->Unicode的转换方法进行字符串的转换,最终将“中文”转换为Unicode的编码方式传递给QString存储。

 

 

Qt大课堂-QtShare

发表评论

您必须登录才能发表评论!