第一階段 盤古開天辟地——ASCII碼
計算機大家都知道,本質是二進制運算和存儲。在計算機中人類的幾乎所有文字和字符都沒法直接表示,所以美國人在發明計算機的時候為了讓計算機可以用於保存和傳輸文字,就發明了ASCII碼(American Standard Code for Information Interchange,美國信息交換標准代碼),用128個數字分別映射到美國人常用的一些字符,包括阿拉伯數字和26個英文字母的大小寫,以及其他一些常用的符號。一個字符占用7位二進制,用一個字節byte(8位)來存儲。
這樣一來就滿足了美國人的交流需要。
第二階段 軍閥混戰之戰國七雄——GBK
但是計算機這東西真的是太好用了,只在美國使用有點暴殄天物,其他國家為了能讓計算機在自己國家也好用,就發明了自己的編碼系統來適配自己國家的文字和字符,比如西歐國家的Windows-1252和中國的GBK以及港澳台地區用得比較多的Big5。
因為ASCII只有128個,用二進制7位就夠了,而計算機的最小存儲單位byte是8位,所以ASCII碼中每個字節的最高位永遠是0,因此這些國家在搞自己的編碼的時候很容易地就保持了對ASCII的兼容,即當每個字節最低位為0的時候表示ASCII碼,為1的時候表示自己國家的編碼。
以中文GBK為例,GBK使用固定的兩個字節,當解析到的字節最高位為0時,只解析當前字節作為ASCII碼,當最高位為1的時候,就將下一個字節讀進來一起解析為一個漢字,而不用考慮第二個字節的最高位,然后跳到第三個字節繼續解析,往復如此直到解析完成。
其他國家和地區的編碼系統也大致如此。
第三階段 秦王掃六合之書同文——Unicode
現在問題來了,每個國家在設計自己國家的編碼的時候基本上都只考慮了自己國家的需求,導致不同國家之間的編碼互不兼容,這給不同國家之間的交流造成了很大的阻礙和困擾。
這時候就需要有人來干秦始皇干過的“車同軌、書同文”的事了,於是就出現了Unicode。Unicode為世界上所有字符設定了統一並且唯一的數字序號,這個編號范圍從0x000000到0x10FFFF,包括110多萬。也就是說,世界上所有國家的文字和字符都能在Unicode中找到唯一的序號,這樣一來大家就有了統一編碼標准。
第四階段 小篆到簡體中文——UTF-8
但是,Unicode只規定了編號,沒有規定編號到二進制的映射方式。
我們先做最簡單的設想:在計算機中使用固定數量的字節來存儲Unicode,即直接存儲所有的字符編號的二進制編碼。但這樣的話對於編號比較小的那些字符來講高位全都是0,非常浪費空間,再考慮到最常用的字符編號都比較小,這么做的話對於網絡傳輸速度和存儲空間都是極大的浪費,所以我們需要一種變長的Unicode映射方式來節省空間,於是就出現了UTF-8。
UTF-8就是使用變長字節表示,每個字符使用的字節個數與其Unicode編號的大小有關,編號小的使用的字節就少,編號大的使用的字節就多,使用的字節個數從1到4個不等。這樣一來存儲同樣多的信息需要的內存空間就便少了很多,在網絡通信中獲取信息的效率也會提升很多。
UTF-8的優勢如此明顯,所以它很快就席卷了全球,成為了大家最常用的編碼。
一點補充:
UTF-8編碼中,大多數中文占用的字節數是3個,相較於GBK的2個,多出了50%,這也就意味着在存儲純中文文檔的時候,UTF-8編碼的文件占用空間是GBK編碼文件的1.5倍。
基於上面這項特性,GBK在很多純中文系統中還是有很大的應用市場。其目的呢,就是為了節省用戶的硬盤和流量
兩點補充(關於java中的char):
Unicode給世界上每個字符分配了一個編號,編號范圍從0x000000到0x10FFFF。編號范圍在0x0000到0xFFFF之間的字符,為常用字符集,稱BMP(Basic Multilingual Plane)字符。編號范圍在0x10000到0x10FFFF之間的字符叫做增補字符(supplementary character)。
Unicode主要規定了編號,但沒有規定如何把編號映射為二進制,UTF-16是一種編碼方式,或者叫映射方式,它將編號映射為兩個或四個字節,對BMP字符,它直接用兩個字節表示,對於增補字符,使用四個字節,前兩個字節叫高代理項(high surrogate),范圍從0xD800到0xDBFF,后兩個字節叫低代理項(low surrogate),范圍從0xDC00到0xDFFF,UTF-16定義了一個公式,可以將編號與四字節表示進行相互轉換。
Java內部采用UTF-16編碼,char表示一個字符,但只能表示BMP中的字符,對於增補字符,需要使用兩個char表示,一個表示高代理項,一個表示低代理項。
使用int可以表示任意一個Unicode字符,低21位表示Unicode編號,高11位設為0。整數編號在Unicode中一般稱為代碼點(Code Point),表示一個Unicode字符,與之相對,還有一個詞代碼單元(Code Unit)表示一個char。
java中char的包裝類Character就封裝了很多與此有關的靜態方法,搞清楚這些定義有助於我們更好地理解和使用Character