编码(ACSIIunicodUTF-8)、QT输出中⽂乱码深⼊分析
总结:
1. qt输出中⽂乱码原因分析
qt的编程环境默认是utf-8编码格式(关于编码见下⽂知识要点⼀);
cout << "中⽂" << endl;
程序运⾏,程序并不认识ANSI,UTF-8以及任何其他编码.系统只知道处理你给它的字符的⼆进制表⽰.
关于  "中""⽂" 的3种编码⼆进制内容:
ANSI(GBK): 0xd6d0  0xcec4
UTF-8: 0xe4b8ad 0xe69687
Unicode: 0x4e2d 0x6587
1)在简体中⽂Windows下的控制台显⽰环境是ANSI编码(代码页936, GBK),先明确这点.
重点区别,MinGW看到的是"0xe4b8ad"和"0xe69687"(gcc默认UTF-8).注意,⽤MinGW编译的源⽂件中有中⽂宽字符必须保存为UTF-8编码.
2)测试代码:
#include <iostream>
using namespace std;
int main()
{
char a[] = "中⽂";
cout << a << endl;
return0;
}
3)经在qt5.8中测试乱码;
分析:参见(下⽂知识要点⼀,知识要点⼆)不难发现UTF-8只是⼀种编码实⾏⽅案,并不是实际编码;再参见(知识要点五),程序运⾏是能过最后编译完成的⼆进制码输出
在vs2017中,⽤unicode编码⽅式,编译运⾏输出正常;原因我想很好理解了,当程序编译后保存的是“中⽂”unicode⼆进制编码,⽽控制台输出时CodePage (GBK 936) 这个CodePage就会根据映射表去⼀⼀对应GBK中的中⽂字,再进⾏输出;
⽽在qt5.8(MinGW)中,输出则是乱码;因为qt5.8默认的编码⽅式是UTF-8;当程序编译后保存的是“中⽂”UTF-8⼆进制编码,⽽控制台输出时CodePage (GBK 936) 这个CodePage就会根据映射表去⼀⼀对应GBK中的中⽂字,好像哪⾥不对,好了,问题就出在这⼉了,CodePage 是各国与unicode的映射表,并不是与UTF-8的(知识要点⼆CodePage),在qt5.8(MinGW)中,原程被编译⼆进制⽂件,保存下来的“中⽂”地址是,UTF-8编码,⽽映射表是在unicode中内容,再进⾏输出,⾃然就是乱码;
⽹上解决⽅法1.修改注册表CodePage 65001  经测试还是乱码
理论分析:CodePage(GBK 936)不到映射,那么把控制台换成UTF-8;那么然先保存的,UTF-8中⽂,再通过UTF-8对应的汉字码,不就能输出汉字;理论好像可⾏,但在我的win7 64位中⽂系统上,qt5.8,vs2017均失败;
可能性原因:我系统中cmd控制台并不⽀持UTF-8编码⽅式(有机会在win10中测试后再做补充)
解决⽅法2:通过(知识点⼀,⼆,五),总结,当要在控制台进⾏中⽂输出时,编码⽅式应该保存为unicode,或ACSI(GBK);
4)关于宽字节输出乱码的问题;
输出宽字节中⽂(详见知识要点四):例
#include <iostream>
using namespace std;
int main()
{
wcout << L"中⽂" << endl;
return0;
}
输出则要⽤wcout⽽不能是cout;关于宽字符详见;知识要点⼆后续,知识要点三
在vs2017中,输出中⽂,为空;
1、cout和wcout
 在C++下,cout可以直接输出中⽂,但对于wcout却不⾏。对于wcout,需要将其locale设为本地语⾔才能输出中⽂:
 wcout.imbue(locale(locale(),"",LC_CTYPE));
 也有⼈⽤如下语句的,但这会改变wcout的所有locale设置,⽐如数字“1234”会输出为“1,234”。
 wcout.imbue(locale(""));
 在C语⾔下,locale设置为本地语⾔(C语⾔中只有全局locale)就可以正常输出了:
 setlocale(LC_CTYPE, "");
 在qt5.8(MinGW)环境中,以上并不实⽤,⽬前还没到输出中⽂的⽅法,未完待续;
知识要点⼀:编码
ASCII: 早期的字符集,7位,128个字符,包括⼤⼩写a-z字母,0-9数字以及⼀些控制字符.
  扩展ASCII: 1个字节8位,只⽤7位不合理.于是第8位⽤于扩展ASCII字符集,这样就⼜多了128个字符.于是⽤着后128个字符来扩展表⽰如拉丁字母,希腊字母等特殊符号.但问题是欧洲那⼀票国家很多互相都拥有不相同的特殊字母,⼀起塞进后128个明显不够,于是代码页出现了.
  Code Page(代码页): 1个字节前128个字符⼤家统⼀和ASCII⼀样,⽽后128个字符,根据不同系统所谓代码页来区分各个语⾔不相同的字母和符号.
  DBCS(双字节字符集): 对于亚洲国家,后128个字符依然⽆法包含⼤量的象形⽂字,DBCS正是为此的⼀个解决⽅案.DBCS由⼀个或两个字节表⽰⼀个字符,这说明DBCS并不⼀定是两个字节,对于如英⽂字母,是向ASCII兼容的,依然由1个字节表⽰,⽽对于如中⽂则⽤2个字节表⽰.英⽂和中⽂可以统⼀地处理,⽽区分是否为中⽂编码的⽅法是2个字节中的⾼字节的⾸位为1,就必须检查后⾯跟随的那个字节,2个字节⼀起解释为1个字符.GB2312,GBK到GB18030都属于DBCS.另外,简体中⽂Windows下的ANSI编码通常是指GBK(代码页936).
DBCS很⼤问题在于字符串的字符数不能通过字节数来决定,如"中⽂abc",字符数是5,⽽字节数是7.对于⽤++或--运算符来遍历字符串的程序员来说,这简直就是梦魇!
  Unicode: 学名为"Universal Multiple-Octet Coded Character Set",简称"UCS".UCS可以看作是"Unicode Character Set"的缩写.
也是⼀种字符集/字符编码⽅法,它统⼀⽤唯⼀的字符集来包含这个星球上多数语⾔的书写系统.UCS向ASCII兼容(即前128个字符是⼀致的),但并不兼容DBCS,因为其他字符在UCS中被重新编码(重新安排位置).
UCS有两种格式:UCS-2和UCS-4.前者⽤2个字节(16位)编码,后者⽤4个字节(实际上只⽤31位)编码.USC-4前2个字节都为0的部分称
为BMP(基本多语⾔平⾯),就是说BMP去掉前2个零字节就是UCS-2.⽬前的UCS-4规范中还没有任何字符被分配在BMP之外.(说⽩了,USC-4就是为当16位的USC-2都被分配完时候做再做扩展⽤的,现在还没⽤到)
  UTF-8,UTF-16,UTF-32: "Unicode transformation format"(UTF) ,即Unicode的传输格式.Unicode规定了怎么编码字符,⽽UTF规定怎么将⼀个Unicode字符单元映射到字节序来传输或保存.
UTF-16和UTF-32分别表⽰以16位和32位为⼀个Unicode单元进⾏编码,其实UTF-16对应就是UCS-2,UTF-32对应就是UCS-4(UCS-2和UCS-4是陈旧的说法,应抛弃). 另外,通常说的Unicode就是指UTF-16.
UTF-8是关键!如果统⼀Unicode都⽤2字节表⽰,英⽂字母觉得⾃⼰就很吃亏(⾼字节始终是0字节).UTF-
8提供了⼀种灵活的解决办法:以单字节(8bit)作为编码单元,变长多字节编码⽅式.如ASCII字母继续使⽤1字节储存,中⽂汉字⽤3字节储存,其他最多可直6字节.
UTF-16和UTF-32需要有字节序标志(FEFF)解决⼤端⼩端问题.UTF-8没有字节序的问题(因为以1个字节为单元).
===============================================================================
其他注意点:
DBCS准确说,应该是MBCS(Multi-Byte Chactacter System, 多字节字符系统).
字符集(Charset)和编码(Encoding)注意区别.如GBK,GB2312以及Unicode都既是字符集,也是编码⽅式,⽽UTF-8只是编码⽅式,并不是字符集.
Linux下The GUN C Library(从glibc 2.2开始)中宽字符wchar_t是以32位的Unicode(USC-4)表⽰.如宽字符"中"字为 "0x00004e2d".⽽
Windows下的CRT使⽤宽字符仍是16位的.
知识要点⼆:关于Unicode的认知(加深对编码的理解)
析Unicode和UTF-8 
⼀、⾸先说明⼀下现在常⽤的⼀些编码⽅案:
1. 在中国,⼤陆最常⽤的就是GBK18030编码,除此之外还有GBK,GB2312,这⼏个编码的关系是这样的。
最早制定的汉字编码是GB2312,包括6763个汉字和682个其它符号
95年重新修订了编码,命名GBK1.0,共收录了21886个符号。
之后⼜推出了GBK18030编码,共收录了27484个汉字,同时还收录了藏⽂、蒙⽂、维吾尔⽂等主要的少数民族⽂字,现在WINDOWS平台必需要⽀持GBK18030编码。
按照GBK18030、GBK、GB2312的顺序,3种编码是向下兼容,同⼀个汉字在三个编码⽅案中是相同的编码。
2.  台湾,⾹港等地使⽤的是BIG5编码
3.  ⽇本:SJIS编码
⼆、Unicode
  如果把各种⽂字编码形容为各地的⽅⾔,那么Unicode就是世界各国合作开发的⼀种语⾔。
  在这种语⾔环境下,不会再有语⾔的编码冲突,在同屏下,可以显⽰任何语⾔的内容,这就是Unicode的最⼤好处。
  那么Unicode是如何编码的呢?其实⾮常简单。
  就是将世界上所有的⽂字⽤2个字节统⼀进⾏编码。可能你会问,2个字节最多能够表⽰65536个编码,够⽤吗?
  韩国和⽇本的⼤部分汉字都是从中国传播过去的,字型是完全⼀样的。
  ⽐如:“⽂”字,GBK和SJIS中都是同⼀个汉字,只是编码不同⽽已。
  那样,像这样统⼀编码,2个字节就已经⾜够容纳世界上所有的语⾔的⼤部分⽂字了。
UCS-2 与UCS-4
  Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。
  现在⽤的是UCS-2,即2个字节编码,⽽UCS-4是为了防⽌将来2个字节不够⽤才开发的。UCS-2也称为基本多⽂种平⾯。
  UCS-2转换到UCS-4只是简单的在前⾯加2个字节0。
  UCS-4则主要⽤于保存辅助平⾯,例如Unicode 4.0中的第⼆辅助平⾯
  20000-20FFF - 21000-21FFF - 22000-22FFF - 23000-23FFF - 24000-24FFF - 25000-25FFF -   26000-26FFF   - 27000-27FFF -28000-28FFF - 29000-29FFF - 2A000-2AFFF - 2F000-2FFFF
  总共增加了16个辅助平⾯,由原先的65536个编码扩展⾄将近100万编码。
三、兼容codepage
  那么既然统⼀了编码,如何兼容原先各国的⽂字编码呢?
  这个时候就需要codepage了。
  什么是codepage?codepage就是各国的⽂字编码和Unicode之间的映射表。
  ⽐如简体中⽂和Unicode的映射表就是CP936,点这⾥官⽅的映射表。
  以下是⼏个常⽤的codepage,相应的修改上⾯的地址的数字即可。
  codepage=936 简体中⽂GBK
  codepage=950 繁体中⽂BIG5
  codepage=437 美国/加拿⼤英语
  codepage=932 ⽇⽂
  codepage=949 韩⽂
  codepage=866 俄⽂
  codepage=65001 unicode UFT-8
最后⼀个65001,据个⼈理解,应该只是⼀个虚拟的映射表,实际只是⼀个算法⽽已。
从936中随意取⼀⾏,例如:
0x9993 0x6ABD #CJK UNIFIED IDEOGRAPH
前⾯的编码是GBK的编码,后⾯的是Unicode。
通过查这张表,就能简单的实现GBK和Unicode之间的转换。
四、UTF-8
  现在明⽩了Unicode,那么UTF-8⼜是什么呢?⼜为什么会出现UTF-8呢?
  ASCII转换成UCS-2,只是在编码前插⼊⼀个0x0。⽤这些编码,会包括⼀些控制符,⽐如 '' 或 '/',这在UNIX和⼀些C函数中,将会产⽣严重错误。因此可以肯定,UCS-2不适合作为Unicode的外部编码。
  因此,才诞⽣了UTF-8。那么UTF-8是如何编码的?⼜是如何解决UCS-2的问题呢?
例:
E4 BD A0        11100100 10111101 10100000
这是“你”字的UTF-8编码
4F 60          01001111 01100000
这是“你”的Unicode编码
关于汉字按照UTF-8的编码规则,分解如下:xxxx0100 xx111101 xx100000
把除了x之外的数字拼接在⼀起,就变成“你”的Unicode编码了。
注意UTF-8的最前⾯3个1,表⽰整个UTF-8串是由3个字节构成的。
经过UTF-8编码之后,再也不会出现敏感字符了,因为最⾼位始终为1。
以下是Unicode和UTF-8之间的转换关系表:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
unicode编码转换二进制U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx、
Unicode编码转换到UTF-8,针对中⽂,简单的把Unicode字节流套到x中就变成UTF-8了。
续篇:
unicode在windows api中的应⽤
实际上,常提到的Win32 API的名称并不是它们的真实名称。这些名称仅仅是⼀些宏,你可以在PSDK的头⽂件中到这些宏对⽤的函数名称。所以,如果PSDK的⽂档提到⼀个函数,如CreateFile,开发⼈员应该意识到它仅仅是⼀个宏。它的真实名称是CreateFileA和CreateFileW。是的,它代表了“两个”函数名,⽽不是⼀个,是同⼀个函数在不同Win32函数的两个不同的版本。以'A'结尾的函数接受ANSI 字符串(char *),即Unicode字符串(wchar_t *)⽽在vs中可以⽤WCHAR宏代替,即wchar_ts型字符串。两种版本的函数都在模块kernel32.dll 中实现,如果你的编程环境是Unicode则,则宏CreateFile在编译是会被CreateFileW代替,否则⽤CreateFileA代替。
PSDK的字符串解决⽅案:TCHARs
为了避免为不同的windows操作系统开发不同版本的PSDK,微软制订了⼀个统⼀的字符串类型TCHARs。TCHAR以及其他的相应的宏在头⽂件WinNT.h中有定义。程序员在程序中不需要为使⽤char还是wchar_t⽽纠结,只需要使⽤宏TCHAR就可以了。根据Unicode环境是否存在,编译器会⾃动进⾏相应的转换。同样道理,程序员不需要为使⽤'A'还是'W'型Win32 API函数纠结。
对于较早期的系统均采⽤ACSI编码,⽽在新型系统中则都统⼀为unicode编码(如:⼿机系统)
知识要点三: L"......", _T(), _TEXT ,TEXT()
L"......": L是表⽰字符串资源转为宽字符的保存(通常转为unicode),却未必是unicode字符,这与编译器实现相关。
_T(" ……") 是⼀个适配的宏    #ifdef _UNICODE(当系统环境是unicod下) _T就是L  ⽽当系统环境是ACSI  _T就是ANSI的。(有便于早期windows系编程⽂件的移植,达到新旧系统交互)
_T、_TEXT、TEXT 三者效果相同
tchar.h是运⾏时的头⽂件,_T、_TEXT 根据_UNICODE来确定宏
winnt.h是Win的头⽂件根据,TEXT 根据UNICODE 来确定宏
如果需要同时使⽤这3个宏,则需同时定义 UNICODE 和 _UNICODE
VS2010以后的版本中,设置:项⽬–属性–配置属性–常规–字符集–使⽤Unicode字符集,那么编译器命令选项中的确同时加⼊了
_UNICODE和UNICODE。
知识要点四: c++ 的cout 与 wcout
cout << "hello world!" << endl; //ACSI 编码输出
cout << L“hello world!” <<endl;// unicode 输出
当输出双字节编码到控制台时,cout输出的将是地址⽽并⾮内容这时就要⽤到wcout;
改为:
cout << "hello world!" << endl; //ACSI 编码输出
wcout << L“hello world!” <<endl;// unicode 输出
知识要点五:编译连接过程
1.预处理⽣成.i⽂件
C++的预处理是指在C++程序源代码被编译之前,由预处理器对C++程序源代码进⾏的处理。这个过程并不对程序的源代码进⾏解析。
这⾥的预处理器(preprocessor)是指真正的编译开始之前由编译器调⽤的⼀个独⽴程序。
预处理器主要负责以下的⼏处
1.宏的替换
2.删除注释
3.处理预处理指令,如#include,#ifdef
2.编译和优化⽣成汇编.s原⽂件
词法分析 -- 识别单词,确认词类;⽐如int i;知道int是⼀个类型,i是⼀个关键字以及判断i的名字是否合法
语法分析 -- 识别短语和句型的语法属性;
语义分析 -- 确认单词、短语和句型的语义特征;
代码优化 -- 修辞、⽂本编辑;
代码⽣成 -- ⽣成译⽂。
3.⽣成.o⽬标⽂件
汇编过程实际上指把汇编语⾔代码翻译成⽬标机器指令的过程。
在最终的⽬标⽂件中
除了拥有⾃⼰的数据和⼆进制代码之外,还要⾄少提供2个表:未解决符号表和导出符号表,分别告诉链接器⾃⼰需要什么和能够提供什么。
编译器把⼀个cpp编译为⽬标⽂件的时候,除了要在⽬标⽂件⾥写⼊cpp⾥包含的数据和代码,还要⾄少提供3个表:未解决符号表,导出符号表和地址重定向表。
未解决符号表提供了所有在该编译单元⾥引⽤但是定义并不在本编译单元⾥的符号及其出现的地址。
导出符号表提供了本编译单元具有定义,并且愿意提供给其他编译单元使⽤的符号及其地址。
地址重定向表提供了本编译单元所有对⾃⾝地址的引⽤的记录。
4.链接
由汇编程序⽣成的⽬标⽂件并不能⽴即就被执⾏,其中可能还有许多没有解决的问题。例如,某个源⽂
件中的函数可能引⽤了另⼀个源⽂件中定义的某个符号(如变量或者函数调⽤等);在程序中可能调⽤了某个库⽂件中的函数,等等。所有的这些问题,都需要经链接程序的处理⽅能得以解决。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。