当前位置: 首页 > 科技观察

彻底解决QT中汉字乱码问题

时间:2023-03-18 11:38:37 科技观察

看完这篇文章,你就可以全面了解Windows下汉字乱码问题了。一劳永逸地解决了这个困扰很多同学的问题。前言在桌面开发的过程中,由于Qt的跨平台特性和更高级的库封装。和MFC比起来不知道好用多少。Qt独创的信号槽机制也大大方便了开发者。它让开发者更专注于业务的逻辑,而不是语言和库的各种细节。但是在使用的过程中,很多朋友遇到了中文Windows系统下乱码的问题。真是让人头疼。上网查了一下,有时候问题可以解决,有时候不知道什么原因就出现奇怪的问题。同样的问题也会出现在cocos2d-x中。小伙伴们不要气馁,这个问题连老板们都头疼。哈哈~~,请看下面的案例。以上问题来自《Cocos2d-x实战:C++卷》,老大也很无奈。今天,让我们自己来剖析这个问题。并最终找到一劳永逸的解决方案。在我们开始之前,让我们列出一些我们遇到的情况:完全正常。(字符爆炸啊)直接乱码。(嘿,倒霉)编译错误-C4819、C2001、C2143。(这是不是违反神了?)小心使用,可能是正常的。有时候是正常的,有时候编译报错,有时候最后的字是乱码,但是前面的都是正常的。(这到底是什么)。细心的小伙伴还总结出偶数个汉字是正常的,奇数个就不正常了。后面加一个英文字符,前面显示正常,最后一个字符乱码。(我也太难了~~~)1、字符编码要想彻底理解这个问题,我们需要从字符编码说起。朋友们,耐心一点。这其实很好理解。说白了,字符编码就是一个对照表。1.1ASCII编码这种编码很简单,只用一个字节编码,只能表示英文字符和标点符号。这里就不赘述了,百度里面有很多文章都有详细的解释。ASCII码表1.2中文码计算机刚发明时,只有ASCII码。也就是说,只有英文,怎么办?没有人可以为我们做这件事,所以我们必须自己做。1980年,国家标准局发布了GB2312,实际上是中文编码对照表。这并不是一件很复杂的事情,因为单个字节只有256种可能,也就是最多只能表示256个字符。然后我们将再使用一个字节。在GB2312中,中文是用2个字节来表示的。2^16=65536,有这么多的可能性,编码汉字绰绰有余。当然,考虑到ASCII编码的兼容性,当第一个字符数小于127时,就表示一个ASCII字符,一个字节就够了。当第一个字符大于127时,需要结合第二个字符来判断是哪个汉字。GB2312一开始编了6000多个汉字,后来发现不够用,又增加了20000多个汉字(包括繁体字),并将编码方案的名称改为GBK。后来又增加了数千个少数民族文字,编码方案的名称也改为GB18030。至此,我们知道GB2312、GBK、GB18030的编码方式在同一行。为了后面的描述方便,我们统称为GBK编码。1.3Unicode编码在中国使用GBK编码方案的同时,其他国家和地区为了使用自己的文字,也在对自己的语言文字进行编码。结果就是不通用!在不同语音的操作系统下编辑的文档,在另一台不同语音的电脑上打开时,会出现乱码。随着全球化的发展,迫切需要一种统一的编码方案来解决这种混乱局面。最后,ISO提出了Unicode编码,放弃了所有区域编码方案。重新编码,所有字符都存储在2个字节中。GBK编码方案与Unicode编码完全不同,这也是乱码的来源。2、文件编码2.1UTF-8编码虽然上面说了ISO对字符进行了重新编码,发布了Unicode。每个字符编码为2个字节,16位。对于使用英语的国家,使用原始的ASCII编码,因此所有文件大小都会增加一倍。这个浪费太大了,于是出现了UTF-8。如果用文字描述UTF-8,就有点复杂了。让我们举个例子,它会很容易理解。例如“中”字的Unicode编码为:0x4E2D。是用二进制写的(0100-1110-0010-1101),那么用UTF-8怎么表示呢?111001001011100010101101让我解释一下。在第一个字节中,前4位有3个连续的1,也就是说这个字符需要由3个字节组成。第二个字节,前2位10,表示与前一个字节相连,后6位为代码。第三个字节和第二个字节一样,前面的10和后面的编码。也就是说将16位的Unicode编码分散成3个字节。好麻烦……确实,遇到中文或者其他多字节编码的字符有点麻烦,但是如果是英文字符,直接用ASCII编码保存。它与原始英文文档直接完全兼容。他们有很多优点。没办法,毕竟电脑技术是他们出的。2.2ANSI编码这个编码是什么?细心的朋友会发现,在Windows系统上用记事本编辑文件,点击另存为,右下角的默认编码是ANSI。这就是Windows为兼容各种编码而做的事情。其实他的方法很简单。如果遇到小于127的编码,就是ASCII码。计算机可以理解这段代码。对于大于127的代码,不用那么担心,直接保存即可。3、一劳永逸地消除乱码。在解决问题之前,我们先来了解一下背景知识,小伙伴们别着急!3.1UTF-8和UTF-8-BOMUTF-8已经够复杂了,那UTF-8-BOM呢??其实不用着急,这也很简单。我们先看一个例子:上图中,我们在记事本中写下“Chinese”,然后用utf-8保存。然后用notepad++查看存储的内容,并以16进制显示。这没问题。但是,如果再次打开,它还会正确显示吗?记事本怎么知道我们存的是utf-8呢?这个十六进制字符串如果用GBK解码,就会“跳”起来,是不是很眼熟?当我们遇到乱码的时候,往往就是这种相似的字符。现在,记事本工作正常,但他有时会承认错误吗?真知道,用记事本新建一个文本文件,输入“中国联通”,保存,再打开。看到微软对中国联通的恶意了吗?呵呵O(∩_∩)呵呵~其实各种软件在处理文本文件的时候经常出错!为了解决这个问题,引入了utf-8-bom。方法很简单。在utf-8文件的开头加上efbbbf三个字节,表示这是一个utf-8文件,告诉软件你不要搞错了。3.2编辑器和编译器对文件的处理在Qt5+VS环境下,编辑器解析我们的源文件完全没有问题。遗憾的是VS编译器并没有像我们想象的那样工作。VS编译器经常错误地将utf-8文件识别为ANSI文件。曾经有小伙伴向微软提交了这个bug,得到了如下回复:编译器在遇到没有BOM的源文件时,编译器会提前读取文件一定距离,看是否能检测到任何Unicode字符——它专门查找UTF-16和UTF-16BE-如果没有找到,则假定它具有MBCS。我怀疑在这种情况下,在这种情况下,它会退回到MBCS,这就是导致问题的原因。翻译一下,当编译器遇到没有BOM的utf-8文件时,会读取一部分判断是UTF-16还是UTF-16BE,如果不是则按照MBCS的方式处理。它根本不判断utf-8文件。Qt默认保存utf-8文件不带bom。然后,按照MBCS的方式进行识别,在我们的环境中,按照ANSI的方式进行处理。好家伙,,,懒得给我们添麻烦了。。。之前微软针对这个问题,有办法在文件开头加上#pragmaexecution_character_set("utf-8"),后来放弃了.至此,我们明白了Qt默认保存的utf-8文件(没有bom)被VS编译器识别为ANSI格式文件,这就是乱码的来源。3.3Qt中的QString处理汉字Qt有一个QString字符串类,使用起来非常方便。我们经常会用到两个常用的方法:QStringstr1("Chinese");QStringstr2=QString::fromLocal8Bit("Chinese");需要明确的是第一个方法,即QString默认构造函数,接受utf-8字节序列。第二种方法接受GBK字节序列。3.4解决方法到此结束,相信大家已经猜到如何解决中文乱码问题了。即:将Qt中所有保存的文件设置为utf-8-bom格式,需要使用中文的地方,需要使用QString::fromLocal8Bit()方法。3.5编译错误的问题至此,细心的朋友就会发现,虽然我们的乱码问题已经解决了,但是前四种现象中的后两种我们还是不明白。在这里,我再给大家解释一下。char*str="中文";看上面的代码,如果我们保存为utf-8文件,但是编译器将我们的文件识别为ANSI格式,也就是中文部分安装了GBK来解析。我们看一下“中文”这三个字的utf-8编码,e4b8ade69687e4b8ad这三个汉字被编码成9个字节,编译器按照GBK编码进行解析,因为在GBK中encoding,汉字需要两个字节,所以后面的分号被吞掉了。如果源文件少了一个分号,肯定编译不通过。还剩最后一题,如果是char*str="Chinese";后面多了一个空格,引号里面的utf编码是:e4b8ade69687e4b8ad20正好是10个字节,所以,编译没有报错,但是在编译器编译的过程中,它是按照GBK解析的。解析到最后,遇到ad20,发现在GBK中找不到对应的字符,就用3f(?)代替ad20,替换。QString接收到的字符序列变为:e4b8ade69687e4b83f因此,QString接收到的utf-8序列的最后一个字节发生了变化,最后一个字符显示为乱码。其实文章开头提到的第一种情况是没有问题的。很有可能是程序比较简单,汉字的个数正好是偶数。真是人品爆表。在2个误会的情况下,我得到了正确的结果。O(∩_∩)O哈哈~4.小结今天介绍了各种字符编码,文件存储编码,VS编译器,QString对字符的处理。终于理顺了乱码的原因。最后的原因是Qt默认保存的是没有bom的utf-8文件,而VS编译器在解析utf-8文件的过程中偷懒,误认为是ANSI编码文件造成的。Cocos2d-x中的乱码,相信小伙伴们已经明白是怎么回事了。