=======================事情是這樣的==========================
在調試某程序時,發生了這樣的錯誤:
char 與 LPCWSTR 類型不兼容
搜索了一下發現是字符集的原因。
項目>>屬性>>字符集>>使用多字節字符集
或者,每個字符串“xxxxx”改爲_T("xxxxx"),char類型改爲wchar_t 或者 LPCSTR
即可解決。
於是深入探究一下字符集的相關
=======================以下正文==============================
計算機中每一個字符都是由編碼表示的。
我們最熟悉的 ASCII碼:
全稱:American Standard Code for Information Interchange 美國標準信息交換代碼
(一直以爲是II是羅馬數字2……)
該代碼是由是由美國國家標準學會(American National Standard Institute , ANSI )制定的
後來,又進行了擴展,由最初的7位128個字符擴展到8位256個字符:
再後來,又有其他國家文字的加入,各國都需要有自己的字符集。
於是他們在ASCII的基礎上,制定了自己的字符集,這些由ASCII派生出來的字符集都成爲ASCI字符集,(這些字符集都是以7位128個字符的ASCII爲基礎)
正式的名稱:MBCS(Multi-Byte Chactacter System,即多字節字符系統)
最常見的GB_2312就是其中之一。
以GB_2312爲例:
GB_2312收錄簡化漢字及一般符號、序號、數字、拉丁字母、日文假名、希臘字母、俄文字母、漢語拼音符號、漢語注音字母,共7445個圖形字符。其中包括6763個漢字,其中一級漢字3755個,二級漢字3008個;包括拉丁字母、希臘字母、日文平假名及片假名字母、俄語西裏爾字母在內的682個全角字符。
(科普:漢字大概6800個)
所謂 多字節字符集 的精髓就是,用多個字節的組合來表示代碼。
例如:“連通”兩個字的編碼爲:C1 AC CD A8
C1 AC 表示 “連”,
C1爲leading byte 引導字節,可以理解爲“區號”,當leading byte 小於128時,自動作爲ASCII碼進行處理。
AC爲實際編碼字節,可以理解爲“位號”
分區表示:
GB_2312中對所收漢字進行了“分區”處理,每區含有94個漢字/符號。這種表示方式也稱爲區位碼。
各區包含的字符如下:01-09區爲特殊符號;16-55區爲一級漢字,按拼音排序;56-87區爲二級漢字,按部首/筆畫排序;10-15區及88-94區則未有編碼。
雙字節表示
兩個字節中前面的字節爲第一字節,後面的字節爲第二字節。習慣上稱第一字節爲“高字節”,而稱第二字節爲“低字節”。
“高位字節”使用了0xA1-0xF7(把01-87區的區號加上0xA0),“低位字節”使用了0xA1-0xFE(把01-94加上0xA0)。
以GB2312字符集的第一個漢字“啊”字爲例,它的區號16,位號01,則區位碼是1601,在大多數計算機程序中,高字節和低字節分別加0xA0得到程序的漢字處理編碼0xB0A1。計算公式是:0xB0=0xA0+16,0xA1=0xA0+1。
==========================================================
然而,由於每種語言都制定了自己的字符集,導致最後存在的各種字符集實在太多,在國際交流中要經常轉換字符集非常不便。
Unicode就誕生了。
Unicode(統一碼、萬國碼、單一碼)是計算機科學領域裏的一項業界標準,包括字符集、編碼方案等。Unicode 是爲了解決傳統的字符編碼方案的侷限而產生的,它爲每種語言中的每個字符設定了統一併且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉換、處理的要求。1990年開始研發,1994年正式公佈。
Unicode是一個編碼方案。
從0到一個很大很大的數,每個數表示人類的一個字符,這樣的映射組成的一個集合,就是——通用字符集(Universal Character Set, UCS)
???很大很大的數是多少???
0x0000 至 0x10FFFF
目前Unicode字符分爲17組,也稱17個平面,每個平面有2^16=65536個碼位,一共有17*65536=1114112個。
寫成十六進制:
0x00 0x00 0x00 0x00 ———— 0x00 0x10 0xFF 0xFF
前兩個爲高位字節從0x0000到0x0010共有17個數,表示17個平面。
低位兩個字節表示每個平面的碼位。
???什麼是UCS-2和UCS-4???
2和4表示所佔字節數。
平面0被稱作BMP(Basic Multilingual Plane)——基礎多語言平面。
範圍是:
0x00 0x00 0x00 0x00 ———— 0x00 0x00 0xFF 0xFF
發現前兩個字節全都是0,於是拿掉他們,就成了只佔2個字節的字符集:
0x00 0x00 ———— 0xFF 0xFF
???人類已經定義了多少字符???
在Unicode 5.0.0版本中,已定義的碼位只有238605個,分佈在平面0、平面1、平面2、平面14、平面15、平面16。
其中平面15和16上只是定義了兩個各佔65534個碼位的專用區PUA(Private Use Area),分別是0xF0000-0xFFFFD和0x100000-0x10FFFD。
所謂專用區,就是保留給大家放自定義字符的區域。
舉個栗子:
3 0x0051
屆 0x5c4a
N 0x004e
B 0x0042
A 0x0041
冠 0x51a0
軍 0x519b
轉換成二進制:
3 0000 0000 0101 0001
屆 0101 1100 0100 1010
N 0000 0000 0100 1110
B 0000 0000 0100 0010
A 0000 0000 0100 0001
冠 0101 0001 1010 0000
軍 0101 0001 1001 1011
這樣計算機在讀取的時候會出現一些問題。
當以字節爲單位讀取的時候,結果就變成了:
3 \ J N B A 3 3
因爲,對於一些字符,雙字節組合在一起才能準確表示映射關係,單字節讀取會導致映射錯亂。
然而,當以雙字節爲單位讀取的時候,發現數字和字母的前一個字節全都是0!
這是對資源的浪費。
如何才能避免?
首先想到就是對字符集進行分段處理,數字小的只取後兩位,數字大的再取高位的數,最後用統一的格式寫出。
這就是:
UTF(Unicode Transformation Format)—— Unicode字符集轉換格式。
UTF分爲UTF-8 , UTF-16 , UTF-32 這幾種類型,8、16、32這幾個數字分別代表,以多少個二進制位爲一個單位。
==========================================================
UTF-8:
UTF-8 以一個字節爲單位。
分段表示如下:
右邊可以理解爲一種模板,將數字轉換爲二進制,再拆分填入模板的x中。
3 0000 0000 0101 0001 >>>> 0101 0001
屆 0101 1100 0100 1010 >>>> 1110 0101 1011 0001 1000 1010
N 0000 0000 0100 1110 >>>> 0100 1110
B 0000 0000 0100 0010 >>>> 0100 0010
A 0000 0000 0100 0001 >>>> 0100 0001
冠 0101 0001 1010 0000 >>>> 1110 0101 1000 0110 1010 0000
軍 0101 0001 1001 1011 >>>> 1110 0101 1000 0110 1001 1011
所佔空間由原來的14個字節變成了13個字節。
當然佔空間不是最主要的原因,這裏舉得例子也是具有一定的特殊性。
如果全是漢字,那麼UTF-8所佔空間並沒有多大的優勢。
然而可以發現,以這樣的格式改寫之後,在以單字節爲單位讀取的時候,每個字節的前幾位都有固定的數字,計算機可以更加精確的識別。
這是最主要的原因。
這也是UTF最主要的作用。
==========================================================
UTF-16:
UTF-16則是以兩個字節爲單位進行讀取,需要滿足如下的格式:
Unicode編碼(十六進制) | UTF-16字節流(二進制) |
0000 0000 - 0000 FFFF | xxxx xxxx xxxx xxxx |
0001 0000 - 0010 FFFF | 1101 10yy yyyy yyyy 1101 11zz zzzz zzzz |
具體規則:
令Unicode編碼爲U,
當 U <= 0x 0000 FFFF 時,其UTF-16形式就是U的二進制型。
當 0x 0000 FFFF < U <= 0x 0010 FFFF時,設U' = U - 0x 0001 0000,再將U' 寫成二進制型 yyyy yyyy yyzz zzzz zzzz,最後填入格式中。
按照上述規則,Unicode編碼0x10000-0x10FFFF的UTF-16編碼有兩個字(一個字是兩個字節,16位)
第一個字的高6位是110110,第二個字的高6位是110111。
可見,
第一個字的取值範圍(二進制)是1101 1000 0000 0000 到1101 1011 1111 1111,即0xD800-0xDBFF。
第二個字的取值範圍(二進制)是1101 1100 0000 0000 到1101 1111 1111 1111,即0xDC00-0xDFFF。
在UTF-16規則下,有的字符是一個字,有的字符是兩個字。
一個字的取值範圍是0x0000 - 0xFFFF
在這當中,0xD800 - 0xDFFF 這一區域是兩個字協同表示字符的。
於是將他們保留下來,作爲代理區。
0xD800-0xDBFF 爲 高位替代。
0xDC00-0xDFFF 爲 低位替代。
前文說道。
平面15和平面16爲專用區PUA,範圍是0xF0000-0xFFFFD和0x100000-0x10FFFD。
補上0xFFFFF和0x10FFFF,兩個特殊編碼之後,
編碼範圍即:
0xF0000 - 0x10FFFF
帶入UTF-16的規則中,得到
U'=U-0x10000:
0xE0000 - 0xFFFFF
轉換爲二進制:
1110 0000 0000 0000 0000 - 1111 1111 1111 1111 1111
加入引導位:
1101 1011 1000 0000,1101 1100 0000 0000 --- 1101 1011 1111 1111, 1101 1111 1111 1111
轉換爲十六進制:
0xDB80,0xDC00 --- 0xDBFF,0xDFFF
其高位替代的範圍是:
0xDB80 - 0xDBFF
這個區域是專用區,因此該代理區叫做——高位專用替代
==========================================================
UTF-32:
UTF-32是以4個字節爲單位進行讀取。
這就非常簡單粗暴了。
因爲Unicode編碼最大值爲0x10FFFF,拆成四個字節就是 00 10 FF FF,
所以,忽略佔空間的影響,每個編碼都可以拆成4個字節的形式,並且編碼與字符一一映射。
需要注意的是:
字節序,多字節數據在計算機中存儲或傳輸的數據。
分爲兩種,“大端”(Big Endian, BE)和“小端”(Little Endian, LE)(源於《格列佛遊記》中雞蛋的大頭還是小頭)
Little Endian:將低序字節存儲在起始地址
Big Endian: 將高序字節存儲在起始地址
0x10FFFF的大端BE表示法: 00 10 FF FF
0x10FFFF的小端LE表示法: FF FF 10 00
Unicode標準建議用BOM(Byte Order Mark)來區分字節序,即在傳輸字節流前,先傳輸被作爲BOM的字符“零寬無中斷空格”。
==========================總結===================================
在很久很久以前,有一羣機智的美國人發明了ASCII碼,把英文字母和常用標點都用數字表示出來了。
後來其他種族也想效仿,但是他們的文字太多了,於是這些高智慧種族在ASCII碼的基礎上,發明了一個區位碼的概念。
用兩個字節來表示一個字符。前一個字節爲區號,後一個字節爲位號。這就是多字節字符集。
漸漸地,這座星球上的各個種族紛紛建立了自己的字符集標準,世界一片欣欣向榮。
忽然有一天,某操作系統公司的程序猿終於死在了各國字符集標準相互轉換的路上。
這一死,給這個星球上的生物敲響了警鐘,這時忽然有一個聲音從天空中飄來——
“我們星球需要一個統一的標準!”
於是universal code 誕生了!後來人們就叫他Unicode。
既然有辣麼多的字符需要表示,那我們就把存儲空間放大嘛!一個字節不夠用兩個,兩個字節不夠用四個!
傳輸數據的時候,我一個字節一個字節給你,就用UTF-8標準,兩個兩個給你就用UTF-16標準,4個4個給你就用utf-32標準。
機智的地球人效仿了區位碼的概念,給每個字節的開頭加了個帽子,讓編碼看起來很統一,110110xxxxxxxxxx,110111xxxxxxxxxx……
一看帽子就知道這個代碼來自於哪裏。
經過不斷地完善,Unicode字符集編碼方案就成了現在的樣子。
=========================參考==================================
《Unicode字符集和多字節字符集關係》
http://blog.csdn.net/stephen1315/article/details/7476236
《Big Endian和Little Endian的區別 》
http://blog.chinaunix.net/uid-479984-id-2114895.html
百度百科:Unicode
http://baike.baidu.com/link?url=YBucaohEYmhdERJolWN65Nj9GAizuI27uabIClvHDL8kF0_Ev8OX0OPUgL87sZ4iKBWE15jihIjeHNXig3365K#4_1
知乎問題:《Unicode 和 UTF-8 有何區別?》
http://www.zhihu.com/question/23374078
百度百科:ASCII
http://baike.baidu.com/link?url=8i94jsXps5a4696lex0TehG4n3bFkAtn2Xi5jUbYZVn2JJWSnRGkV49-oKJoVoCnINK1nmnk_ADpBWsGL70wkK
百度百科:ANSI
http://baike.baidu.com/subview/185282/6215666.htm#viewPageContent
百度百科:字節序
http://baike.baidu.com/link?url=Viu2Tt3dwUWj9XxlHXBcl0cVqXuDd63XlEDDhS5Iks2Nti8fi5Cn6zUwOYIF6lbJN_ceGFm9dDgZ8nvlEBZeka