一篇能看懂unicode的文章
@YangYang48
unicode
在開始這個問題之前首先提出幾個問題,希望讀者能帶著這幾個問題去看這篇文章
問題1
一個漢字究竟占幾個字節?
問題2
我在網頁上能看到一些外文像韓文日文或者特殊符號,自己卻打不出來?
問題3
如何理解C/C++中的char、wchar_t 、char16_t和char32_t這幾種類型?
起源
計算機只能處理數字,不能處理文本,那么對計算機操作文本就需要進行一定的編碼。
計算機通過設計8位(1個字節)的二進制編碼進行操作文本,因為計算機不是中國發明的,一開始計算機語言是26英文字母、數字和一些符號,2^8-1=255個編碼已經完全能夠適應,這就是早起的ASCII編碼。
然而隨著世界的發展,并且各個國家都有自己的語言,上述的編碼已經不夠用了,各個國家自己制定自己的文字的編碼規則。中國大陸制定了GB2312,日本就是JIT標準,中國香港,中國臺灣對應的是BIG5標準。
這里的GB2312,采用兩個字節,因此編碼范圍就從0xA1A1 -0xFEFE,這個范圍可以表示23901個漢字,但實際上里面包含6763個漢字和682個符號。這種方法好處就是和ASCII不會發生沖突,并且能夠實現英文和漢字同時顯示。
BIG5,香港和臺灣用的比較多,繁體,范圍: 0xA140 - 0xF9FE, 0xA1A1 - 0xF9FE,每個字由兩個字節組成,其第一字節編碼范圍為0xA1~0xF9,第二字節編碼范圍為0x40-0x7E與0xA1-0xFE,總計收入13868個字(包括5401個常用字、7652 個次常用字、7個擴充字、以及808個各式符號)。
其他國家也會出現類似的問題,那么每個國家的語言就都成“方言”了,只能處理自己國家的文本。很顯然,這樣做不利于國際的溝通和交流。那么,這個時候unicode就出來大顯身手了。unicode通過數字來處理字符,保證前256個字符的同時,而且使得各個國家各個字符都互相兼容。
在表示一個Unicode的字符時,通常會用“U+”然后緊接著一組十六進制的數字來表示這一個字符,那么總的可表示的字符即為2^16-1=65535。那么65535個字符就完全能把世界上語言都裝進去嗎,回答是不能。因為還有其他的十六個平面,我們常用的就是基本文本平面BMP,也叫零號平面。
文本平面的概念就是Unicode中的一個編碼區段。編碼從U+0000至U+FFFF,總共有這樣的17個平面。
上圖表示為第零平面,看起來跟元素周期表類似。有不同的顏色分類,還有未開發未使用的格子。其中每個寫著數字的格子代表256個碼點,不同的顏色對應不同的文本含義,詳見下述闡釋,具體可以參照百度百科。
黑 = 拉丁文字及符號
淺藍 = Linguistic scripts
藍 = 其他歐洲文字
橘 = Middle Eastern and SW Asian scripts
淺橘 = 非洲文字
綠 = 南亞文字
紫 = 東南亞文字
紅 = 東亞文字
淺紅 = 中日韓漢字
黃 = Aboriginal scripts
紫紅 = 符號
深灰 = Diacritics
淺灰 = UTF-16surrogates and private use
藍青 = Miscellaneous characters
白 = 未使用
直觀表示unicode
說了這么多,完全還不是很明白這個到底怎么用?
感覺用不到,平時的話跟我們有什么關系?
還有這個對我學習或者編程有什么幫助?
其實unicode的作用還是很大的,可能平時察覺不到,但我們其實已經用上了。在Windows操作系統當中,以電腦win10為例子。我們在C:\Windows\Fonts中,左側的邊框欄中選出查找字符,可以跳出如下圖所示的字符映射表。仔細觀察左下角可以發現驚喜,所有的字符都有對應的unicode碼值。
例如打開字體幼圓,選擇“拼”這個中文字,左下角顯示U+62FC。通過程序員模塊在計算機中得出轉化為十進制的25340。我們在電腦中打開Ms中的word,按住Alt鍵,然后在敲入25340,可以發現這個時候word出現了“拼”這個字。同理,在“拼”這個字的后面按住Alt鍵然后再按X鍵,可以發現“拼”這個字轉化為四位的十六進制0x62FC,這號對應字符映射表的值。當然也可以嘗試在記事本中同樣操作。
接下來就是部分的編碼表,如下表所示。
因為已經通過上述的操作例子熟悉了unicode編碼,那么下面的表中的值也會很好的理解,第一個漢字為“?”,unicode碼值為U+4400,轉化成十進制為17408,在Ms的Word中按住Alt+17408即可得到這個“?”的漢字。
除此之外,還可以在電腦中設置非unicode程序的語言,在語言和區域中,選擇管理,可以發現如果不支持unicode程序,那么就是用中文設置,如下圖所示。
可以看到在這里出現了Unicode UTF-8,那這又是什么呢?
那么計算機當中一直出現的UTF-8,UTF-16,UTF-32又是什么編碼呢?
編碼方案
UTF-8,UTF-16,UTF-32是數字轉換到程序數據的編碼方案。
這里還得講一個叫UCS的。如果說unicode是要統一所有國家的文本的編碼,這個時候發現另外有一個叫iso的組織也在搞這個,他們的編碼叫做UCS。
unicode和UCS的異同:
unicode有多種編碼方式:UTF-8,UTF-16,UTF-32
UCS有兩種形式:UCS-2和UCS-4
兼容方式:不完全兼容,UTF-16是UCS-2的擴展,UTF-32是UCS-4的子集
這里發現出現了好多新的概念,UCS-2和UCS-4
可以知道,UCS-2是16位,即216=65536個碼值,UCS-4是31位,最高面位0,即231=2147483648個碼值。UCS-4根據最高位為0的最高字節分成27=128個group。每個group再根據次高字節分為256個plane。每個plane根據第3個字節分為256行,每行包含256個cells。當然同一行的cells只是最后一個字節不同,其余都相同。group 0的plane 0被稱作Basic Multilingual Plane, 即BMP。或者說UCS-4中,高兩個字節為0的碼位被稱作BMP。將UCS-4的BMP去掉前面的兩個零字節就得到了UCS-2。
UTF-8
UTF-8是變長度的編碼,主要為了解決符號在網絡中傳輸的浪費問題,就出現了UTF-8編碼。
| 000000-00007F | 0xxxxxxx |
| 000080-0007FF | 110xxxxx 10xxxxxx |
| 000800-00FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
| 010000-10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
從上邊可以發現一些特征,這個UTF-8也是需要unicode碼值的,但是在位數較小的時候,可以得到很簡潔的方式,在計算機程序或者網絡傳輸可以大大增加效率。
舉四個例子來說明這個到底怎么轉化。
example1
在0x00-0x7F范圍內的字符,UTF-8編碼需要7位。
英文字母“A”在unicode編碼是第65個編碼,十六進制表示為0x41,轉化成二進制,在首位添加0,即為0100 0001,這個就是UTF-8二進制表示方法。其實?對于0x00-0x7F之間的字符,UTF-8編碼與ASCII編碼完全相同,用十六進制表示也為0x41。
example2
在000080-0007FF范圍內的字符,UTF-8編碼需要11位。
希臘字母“α”在unicode編碼是第945個編碼,十六進制表示為0x03B1,轉化成二進制,
0011 1011 0001?,取其中的后11位,分別按順序填充至UTF-8二進制的編碼中,即為
11001110 10110001,再轉化成十六進制表示為0xCE 0xB1?,這個即為UTF-8的存儲的程序數據
example3
在000800-00FFFF范圍內的字符,UTF-8編碼需要16位。
中文漢字“博”在unicode編碼是第21338個編碼,十六進制表示為0x535A,轉化成二進制,
0101 0011 0101 1010?,可以發現這個二進制正好是16位,依次按順序填充至UTF-8二進制的編碼中,即為11100101 10001101 10011010,再轉化成十六進制表示為0xE5 0x8D 0x9A,正好為三個字節,可以發現漢字在unicode編碼是二個字節,但是在UTF-8這個范圍內漢字的字節是三個。?
example4
在010000-10FFFF范圍內的字符,UTF-8編碼需要21位。
在這個范圍內的編碼,已經不屬于基本文本平面BMP了,在Msword中可能是顯示不出文本的。
舉一個簡單的十六進制編碼為“0x1011C”,轉化成二進制的形式,0001 0000 0001 0001 1100?,依次按照順序填充至UTF-8二進制的編碼中(這里邊不足21位在首位添加0),即為
11110000 10010000 10000100 10011100,再轉化成十六進制表示為0xF0 0x90 0x84 0x9C,這個范圍內的UTF-8字節數為四個。
那說了這么多UTF-8是其中一種編碼方案,前文起源中也GB2312也是編碼方案。 那么兩者的區別是什么?
通過下面的表格來具體說明UTF-8和GB2312的區別,并且我們一般漢字的網頁都是GB2312的編碼方案,類似騰訊、網易等,UTF-8一般是少數民族頻道,類似藏語,維語等等。
| 通用形式 | 支持目前各個國家的語言 | 只支持中文和常見英文 |
| 相互轉換 | 可以對照編碼規律轉化unicode | 無規律,只能查表轉化unicode |
| 漢字的字節 | 3個字節或者4個字節 | 2個字節 |
| 存儲大小 | 體積較大 | 相對輕小 |
UTF-16
相對于UTF-8來說多數情況是以兩個字節存儲,但UTF-16無法兼容于ASCII編碼。
UTF-16可看成是UCS-2的父集。在沒有輔助平面字符(即其他16個輔助平面)前,UTF-16與UCS-2所指的是同一的意思。
| 0000-0FFFF | 對應unicode編碼 |
| 010000-0EFFFF | 非專用區 |
| 0F0000-10FFFF | 專用區 |
看到這邊就有疑問了,什么是專用區和非專用區,兩者之間的編碼方式有什么區別。
Unicode標準將平面15和平面16都作為專用區
除了基本文本平面以外,UTF-16的編碼,需要經過U’=U-0x10000處理,然后在講他們依次填入高低位,形如110110xxxxxxxxxx 110111xxxxxxxxxx,空缺部分為20位,(即便是U+10FFFF經過
U-0x10000處理得到的也是20位的二進制數。)
高位合并取值范圍是110110 0000000000-110110 1111111111,十六進制范圍為0xD800-0xDBFF
低位合并取值范圍是110111 0000000000-110111 1111111111,十六進制范圍為0xDC00-0xDFFF
| D800-DB7F | 高位替代 |
| DB80-DBFF | 高位專用替代 |
| DC00-DFFF | 低位替代 |
把高位DB80-DBFF范圍和低位DC00-DFFF取出來合并,可以得到總的范圍DB80 DC00-DBFF DFFF
寫成二進制的形式是1101101110000000 1101110000000000-1101101111111111 1101111111111111,
將高低位去除得到范圍1110 0000 0000 0000 0000-1111 1111 1111 1111 1111,十六進制表示
0xE0000-0xFFFFF再經過反向處理U=U’+0x10000得到unicode編碼0xF0000-0x10FFFF。這個正好對應上述的專用區,他們是平面15和平面16。
example1
在基本文本平面BMP中(U<0x10000),UTF-16的編碼就是U對應的16位整數,即跟unicode編碼沒有區別,在這個范圍UTF-16是兩個字節。
“編碼”二字分別對應十六進制0x7F16,0x7801,UTF-16的編碼也為0x7F16和0x7801。
example2
超過基本文本平面BMP(U>0x10000),UTF-16編碼會有明顯的區別,這里的UTF-16為四個字節。
今天日期為2020/3/20,那么取unicode編碼0x20320,經過U’=U-0x10000處理得到0x10320,轉化成二進制的形式為0001 0000 0011 0010 0000?,然后在高低位依次加進去最終得到
1101100001000000 1101111100100000,在轉化成十六進制可以得到0xD840 0xDF20。
通過例子可以知道,目前支撐我們繼續使用 UTF-16 的理由主要是考慮到它是雙字節的,在計算字符串長度、執行索引操作時速度很快。
UTF-32
相對于UTF-16來說多數情況是以四個字節存儲,UTF-32編碼就是其對應的32位的unicode編碼。
UTF-32 是一個 UCS-4 的子集,使用32-位元的碼值,只在0到10FFFF的字碼空間。
這里首先要講的就是字節序,用來表示大端(BE)和小端(LE)。
字節序:指多字節數據在計算機內存中存儲或者網絡傳輸時各字節的存儲順序。
字節序對于單字節的無影響,在多字節中會使用得到,下表通過兩個例子來說明。
| 0x007F16 | 7F16 | 16 7F | 00 00 7F16 | 16 7F 00 00 |
| 0x020320 | D8 40 DF20 | 20 DF 40 D8 | 00 02 03 20 | 20 03 02 00 |
大端(BE)跟我們通常熟悉比較常見的儲存順序,而小端(LE)的存在也能滿足一些方面的需要。
問題回答
1.一個漢字究竟占幾個字節?
準確的來說漢字2-4個字節。不同的編碼方式下得到的是不同的字節。
采用GB2312方式的編碼,字節數為2;
UTF-8編碼是變長編碼,通常漢字占3個字節,擴展B區以后的漢字占四個字節。
2.我在網頁上能看到一些外文像韓文日文或者特殊符號,自己卻打不出來?
1)在Ms的Word中,可以嘗試用Unicode編碼對照系統中韓文的字符映射表來打韓文字,不需要安裝額外的語言包。
2)在瀏覽器中,需要在系統安裝語言包,使得鍵盤可以對照著打印出韓文的字符
3.如何理解C/C++中的char、wchar_t 、char16_t和char32_t這幾種類型?
其中char16_t char32_t是C++當中新增的類型。
wchat_t是C/C++的字符類型,unicode編碼的字符一般以wchar_t類型存儲,在windows下就是unicode碼值的UTF-16編碼值。
例如“慕課網”這三個漢字
對應的unicode碼值為0x6155,0x8BFE,0x7F51
轉化為對應的二進制0110 0001 0101 0101?,1000 1011 1111 1110?,0111 1111 0101 0001
正好對應000800-00FFFF范圍的1110xxxx 10xxxxxx 10xxxxxx碼值區
分別為11100110 10000101 10010101 ,11101000 10101111 10111110,11100111 10111101 10010001
轉化成16進制即為0xE6 8595?,0xE8 AFBE?,0xE7 BD91?
char data_utf8[]={0xE6,0x85,0x95,0xE8,0xAF,0xBE,0xE7,0xBD,0x91};//UTF-8編碼
char16_t data_utf16[]={0x6155,0x8BFE,0x7F51}; //UTF-16編碼
char32_t data_utf32[]={0x00006155,0x00008BFE,0x00007F51};//UTF-32編碼
參考
[1] xiangjuan314,unicode和usc區別
[2] 百度百科“通用字符”
[3] 百度百科“unicode”
[4] softman11,徹底搞懂字符編碼(unicode,mbcs,utf-8,utf-16,utf-32,big endian,little endian…)
[5] Mr_Lsz,C++:wchar_t 和C++新增類型:char16_t char32_t
[6] 大家網大學三年級
[7] nodeathphoenix,寬字符wchar_t和窄字符char區別和相互轉換
[8] xrzx,unicode詳解又一篇
總結
以上是生活随笔為你收集整理的一篇能看懂unicode的文章的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jmeter 性能测试场景
- 下一篇: Linux: cp 复制文件、文件夹到文