读完本文,让你彻底明白Windows下中文乱码的问题。一劳永逸地解决这个困扰很多同学的问题。文末另有福利。
目录
在桌面开发过程中,由于Qt的跨平台特性,以及更加先进的库封装。比起MFC,那用着不知道要爽多少。Qt独创的信号槽机制,也大大方便了开发者。可以把更多的精力放在业务的逻辑上,而不是语言和库的各种细节上。
可是,在使用的过程中,不少朋友在中文Windows系统下,遇到了乱码的问题。着实头痛,网上搜了一圈,有时能解决问题,有时不知道什么原因的情况下又出现了奇怪的问题。同样的问题在cocos2d-x中也会出现。
小伙伴们不要灰心,这个问题连大佬们都头痛。哈哈~~,请看下面的案例。
上面的问题来自《Cocos2d-x实战:C++》卷,大佬也很无奈啊。
今天,让我们来自己剖析一下这个问题。并最终找到一劳永逸的解决方案。
在开始前,我们先来罗列一下遇到的几种情况:
warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
error C2001: 常量中有换行符
error C2143: 语法错误: 缺少“;”(在“return”的前面)
很小心的使用,可能正常。有时正常,有时编译报错,有时末尾的字是乱码,前面的正常。(这是什么鬼啊)。
细心的小伙伴还总结出了,偶数个中文字符正常,奇数个就不行了。后面再加个英文字符,前面的显示正常,后面一个字符乱码。(我也太难了吧~~~)
要彻底理解这个问题,我们需要从字符编码说起,小伙伴们稍微有点耐心,这个其实很容易理解。
这个编码很容易,就用了一个字节进行编码,只能表示英文字符和标点符号。这里我就不过多赘述了,百度一下,就有很多文章有详细讲解。
计算机刚开始被发明的时候,只有ASCII编码。也就是说只有英文,那我们怎么办呢?没有人帮我们做,那只有自己来了,在1980年,国家标准总局发布了GB2312。这也不是很复杂的东西,因为单个字节只有256种可能,也就是说,最多只能表示256种字符。那么我们就再多用一个字节呗,在GB2312中,中文就用2个字节进行表示。2^16 = 65536,有这么多种可能,编码汉字绰绰有余了。
当然,考虑到兼容ASCII编码,当第一个字符数字小于127时,就表示ASCII字符,用一个字节就够了。当遇到第一个字符大于127时,就要结合第二个字符来决定是哪个中文字符了。
刚开始GB2312把6000多个中文编了进去,后来发现不够用,又增加了20000多个字符(包括繁体字),编码方案名称改为GBK。再后来,又增加了几千个少数民族字符,编码方案名称改为GB18030。到这里,我们就知道GB2312、GBK、GB18030的编码方式是一脉相承的。为了后面叙述的方便,我们统称这种为GBK编码。
在中国使用GBK编码方案的同时,其他国家和地区为了使用自己的文字,也纷纷进行自己的编码。造成的结果就是,不通用!不同语音的操作系统下编辑的文档,在另一台不同语音的计算机中打开就是乱码。
随着全球化的发展,急需一种统一的编码方案,来解决这种混乱的局面。
最终,ISO拿出了Unicode编码,废弃所有地区性的编码方案。重新编码,所有的字符统一采用2个字节进行存储。
GBK编码方案和Unicode编码完全不同,这也是乱码的根源。
虽然上文中讲到ISO将字符进行了重新编码,并发布了Unicode。每个字符采用2个字节,16位进行编码。对于使用英语的国家来说,原来采用的是ASCII编码,那么所有的文件大小都会变成原来的2倍。这个浪费太大了,于是UTF-8就出现了。
如果用语言描述UTF-8,有些复杂。我们来举个例子,就很容易明白了。
比如,“中”这个字,Unicode编码为:0x4E2D。用二进制写就是(0100-1110-0010-1101),那么用UTF-8,怎么进行表示呢?
1110 0100
1011 1000
1010 1101
我来解释一下,第一个字节,前面的4位中有连续的3个1,表示这个字符需要有3个字节组成。
第二个字节,前面的2位10,表示上接前面的字节,后面的6位是编码。
第三个字节同第二个字节,前面的10和后面的编码。
也就是说,16位的Unicode编码,被分散到3个字节中。
好麻烦啊……的确,遇到中文或其他多字节编码的字符是有点麻烦,但是如果是英文字符,直接就用ASCII编码保存了。直接完全兼容原来的英文文档,他们就是有这么多的优越性,没办法,毕竟计算机技术来自他们那儿。
这又是什么编码?细心的小伙伴会发现,你在Windows系统上用记事本编辑完文件,点另存为的时候,右下角默认的编码就是ANSI。这是Windows为了兼容各种不同的编码,而这样做的。
其实,他的做法非常简单,如果遇到小于127的编码,就是ASCII编码,计算机都认识这个编码,对于大于127的编码,也不用管那么多了,按原样保存就行了。
再解决问题前,我们再稍微了解一些背景知识,小伙伴们不要着急啊!
UTF-8都够复杂了,还来个UTF-8-BOM??
其实,不必担心,这个也是非常简单的。
让我们先来看个例子:
上图,我们在记事本中写入“中文”,然后,以utf-8保存。
再用notepad++查看存入的内容,以十六进制显示。这样没有问题。
但是,如果再次打开,还会正确显示吗?记事本怎么知道我们是按utf-8存储的呢?如果这个十六进制的串,用GBK解码就是“涓枃”,是不是有点眼熟啊?我们遇到乱码的时候,也经常是这种类似的字符。
现在,记事本工作得好好的,但是他会不会认错呢?还真会,用记事本新建一个文本文件,输入“联通”,保存,再打开。你是不是看到了微软对联通满满的恶意?哈哈O(∩_∩)O哈哈~
其实,各种软件在处理文本文件的时候,经常会搞错!为了解决这种问题,就引入了utf-8-bom。
做法非常简单,再utf-8文件的开头加入ef bb bf 三个字节,标示这是一个utf-8的文件,你可别认错了。
在Qt5 + VS的环境中,编辑器对于我们的源文件解析的完全没有问题。
可惜的是VS的编译器却不是像我们想像的进行工作的。
VS的编译器经常认错utf-8文件为ANSI文件,曾经有小伙伴把这个问题,向微软提交了这个bug,得到的回复如下
The compiler when faced with a source file that does not have a BOM the compiler reads ahead a certain distance into the file to see if it can detect any Unicode characters - it specifically looks for UTF-16 and UTF-16BE - if it doesn't find either then it assumes that it has MBCS. I suspect that in this case that in this case it falls back to MBCS and this is what is causing the problem.
翻译过来,就是当编译器遇到不带BOM的utf-8文件,会读入一部分进行判断是否UTF-16和UTF-16BE,如果不是就按照MBCS方式处理。
它根本就不进行utf-8文件的判断啊,Qt默认保存的就是utf-8文件,并且不带bom。然后,被按照MBCS方式识别,在我们的环境中,就是按照ANSI方式来处理。
好家伙,,,这么偷懒啊,造成了我们无穷的麻烦……
之前微软为了这个问题,还出过在文件开头加上#pragma execution_character_set("utf-8")的方式,后来也被废弃了。
到这里,我们明白了,Qt默认保存的utf-8文件(不带bom),被VS的编译器认成了ANSI格式的文件,就是乱码的根源。
Qt中有QString字符串类,使用非常方便。
经常我们使用2种常用的方式:
QString str1("中文");
QString str2 = QString::fromLocal8Bit("中文");
需要明确的是第一种方法,也就是QString默认构造函数,接受的是utf-8字节序列。第二种方法,接受的是GBK字节序列。
到这里为止,相信大家对怎么解决中文乱码的方案已经猜出来了。
那就是:
在Qt中设置所有保存的文件都是utf-8-bom格式
在需要使用到中文的地方需要使用QString::fromLocal8Bit()方式。
到这里,细心的小伙伴就会意识到,虽然,我们乱码的问题得到了解决,但还是不明白前面4种现象中的后两种是什么情况。这里,我就再给大家解释一下。
char * str = "中文中";
看上面的代码,如果我们保存在utf-8文件中,而编译器把我们的文件认成了ANSI格式的,也就是中文部分安装GBK来解析。我们看“中文中”这三个字的utf-8编码
e4 b8 ad e6 96 87 e4 b8 ad
三个中文字符被编码成了9个字节,在编译器按照GBK编码进行解析,因为GBK中中文字符需要连个字节,就把后面的分号就给吞噬掉了。源文件少了个分号,编译肯定是通不过的。
还剩最后一个问题,如果是
char * str = "中文中 ";
引号中的utf编码为:
e4 b8 ad e6 96 87 e4 b8 ad 20
刚好是10个字节,所以,编译没有报错,但是在编译器编译的过程中,是按照GBK进行解析的,到解析到最后,遇到ad 20,发现找不到GBK中对应的字符,就把ad 20用3f(?),替代。
所以,QString接收到的utf-8序列最后一个字节被改掉了,最后一个字符就显示出了乱码了。
其实,文章的开头提到的第1种没有问题的情况,很有可能,程序比较简单,而中文字符出现的个数刚好是偶数。真是人品大爆发,在发生2次误解的情况下,得到了正确的结果。O(∩_∩)O哈哈~
今天,我们介绍了各种字符编码,文件存储编码,VS编译器,以及QString对字符的处理。总算理顺了出现乱码的原因。
最后,给大家送个福利,如果需要Qt视频教程,可以关注公众号“半夜机叫”,回复“Qt”。如果,有其他问题,也可以在公众号后台给我留言。
windows boot_双端云祥,微笑启动 window
写在前面的话:1、最近很是头疼搞SD卡烧入系统的时候发现识别不了。2、找了很多方法还是发现启动不了。3、后面看友善之臂的官方烧录文档解决问题4、现在总结下制作SD卡启动的步骤。制作SD卡启动步骤1、使用SD-Flasher这个文件将super-boot.bin烧录至SD卡1、打开这个程序如下图所示:2、选中烧入到SD卡的镜像uboot,如上图所示:3、然后使用scan找到要烧入的SD卡,这里的Av..._ubootsd卡启动失灵
在linux的世界开发web应用程序,我们首先需要建立一个开发平台:操作系统: 初级水平建议ubuntu,因为目前用户较多,容易解决问题。最好采用最新的版本,目前主流是8.10开发语言: python,Php都可以作web开发,不推荐Java系列,因为体积庞大,开发是智力的活动你不是体力战。手里的武器只能一定程度的影响发挥,真正的程序员不在乎
作者:丁点helper来源:丁点帮你今天我们开始一个新的主题——生存分析。什么叫生存分析?为什么要采用生存分析呢?前面我们一起学习的多重线性回归和Logistic回归都主要是用来分析某个结果的影响因素,比如教育程度对收入的影响,或者,糖尿病发生与否的影响因素,这些方法主要是在静态地分析某一个特定的结果。可是,倘若我们不仅仅关心结果的发生情况(发病VS未发病),同时我们也想看看发生该结果所经历的时间长短,此时,简单的线性或Logistic回归就难以满足这个需求,而生存分析可以来回答这类似的.._生存分析 非肿瘤死亡
1. gpedit.msc-----组策略 3. Nslookup-------IP地址侦测器 4. explorer-------打开资源管理器 5. logoff---------注销命令 6. tsshutdn-------60秒倒计时关机命令 7. lusrmgr.msc----本机用户和组 8. services.msc--- 9. oo...
【导语】:今天我们来聊聊地摊经济,Python技术部分请看第四部分。Show me data,用数据说话!知乎上有一个问题:疫情结束后,你最想做的一件事是什么?有人这样回答,最想见的人就是家楼下烧烤店的老板;最想做的事,就是来一扎啤酒,来几十个串,一个人慢慢悠悠地吃,然后看着周围的人热热闹闹地聊天。一场疫情,终于不少人明白:原来摇晃的红酒杯,并不是生活的全部。平平淡淡的烟火气才是生活的真谛。最近,带着烟火气的地摊经济,火了。这一切都源于成都的一个尝试:3月,成都就出台政策,允许商户在规定区.
转载自:http://blog.csdn.net/jackfrued/article/details/17339393Java程序员面试题集(1-50)一、Java基础部分1、面向对象的特征有哪些方面?答:面向对象的特征主要有以下几个方面:1)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。2
一、数据的来源一手数据 vs 二手数据1.一手数据(Primary data)也称为原始数据。顾名思义,是指直接获取,没有经过加工或者第三方传递获得的数据。比如传统调研中的问卷测评、 小组访谈、面对面沟通等形式获得的数据,或者是互联网时代用户直接填写的个人信息数据以及平台抓取的行为数据等等。2.二手数据(Secondary data)主要是相对于一手数据而言,指的是通过第三方或者是现有的数据资料获取的数据。比如国家统计局数据、知名文献中 罗列的数据等等。 一手数据的来源渠道,一般比较固定,往往是和_primary data
就目前而言,很多的企业都会使用数据分析来进行企业工作。于是,这就需要企业中的产品经理懂得数据分析,但是很多产品经理并不是数据分析专业的,因此需要学习一些相关的数据分析知识。那么大家知道不知道产品经理怎么学习数据分析工作呢?产品经理怎么用好数据分析呢?下面就由小编为大家解答一下这个问题,希望这篇文章能够给大家大家带来帮助。首先,我们给大家说一下产品经理需要掌握数据分析知识的内容,数据分析知识的内...
上篇我们介绍了Java运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法3个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行出栈和入栈操作,栈帧中分配的内存基本上是在类结构确定下来时就是已知的。因此这几个内存区域的分配和回收都具备稳定性,方法结束或者线程结束,内存相应的就被回收了。垃圾收集所关注的主要是Java堆和方法区这部分内存,因为这部分内存的分配和回收都是动..._gc123456?123456joinfighting
很多同学接触Linux不多,对Linux平台的开发更是一无所知。而现在的趋势越来越表明,作为一 个优秀的软件开发人员,或计算机IT行业从业人员,掌握Linux是一种很重要的谋生资源与手段。下来我将会结合自己的几年的个人开发经验,及对 Linux,更是类UNIX系统,及开源软件文化,谈谈Linux的学习方法与学习中应该注意的一些事。 就如同刚才说的,很多同学以前可能连Linux是什么都不知道
更加应该说是一个普通Win32程序的Hello world。一直以来VC6.0常常是用来考试的,学校所教的程序都是一些DOS界面的控制台程序,即便是到了C++,当初憧憬满满的以为是从DOS界面升级到WIN界面,结果我惊讶地发现,不过是在C上的基础上加了一大堆什么类,然后继承、封装、多态,构造函数、析取函数给你讲一大堆,还有一大堆神人跟你扯int main()是比void main()正确,怒cao_win32 application helloworld