本篇文章講解各種字符編碼的使用和區別,使用上來說,幾乎都是一致的,理解了底層的字符編碼,對於構建跨平台應用來說,是至關注重要的。
再C#中,包含了一下幾種主流的字符編碼,也是目前為止,用的最多的字符編碼了。
- ASCII
- ANSI
- Unicode
- UTF-8
- UTF-32
先來講講歷史的東西,最初的計算機是右美國先發明出來的,我們都知道計算機最重要的功能是用於計算,尤其是數據計算,在當時加減法的計算上比人類快很多很多,用的多了就遇見了個大問題,比如我在程序里經過了一系列復雜的計算,現在要把結果顯示出來,這個就麻煩了,比如1000,在計算機里是 03 E8 (當然有可能也是 E8 03),然后要顯示1000出來(也可以顯示"03 E8"),這下真是麻煩了,做不到,顯示英文字符也做不到,所以
美國國家標准學會主導制定了 ASCII 標准,全稱為American Standard Code for Information Interchange ,畢竟計算機是美國發明出來的,並率先在美國大規模發展和使用的,所以標准帶個美國也是情理之中的。這張表如下:
我們看到除了基本的數字(0-9),大小寫的英文之母外,還定義了標點符號,空格,對於一個字節來說,共有256種組合,美國人發現還沒有用完,就用剩余的字符設置了一系列控制相關的,這些控制相關的再初期非常有用,用來傳送文本,及段落,及制表符的。
指定完上述這張表之后,美國人看到很滿意,完全符合了美國的使用習慣和顯示需求。要想顯示啥就顯示啥,連美元符號都有。但是隨着計算機的普及,以及傳入到歐洲,傳入到亞洲使用,就又遇見了一個巨大的問題,字節使用僅剩下了0x80-0xFF沒有使用,但是有大量的歐洲字符,亞洲字符需要來表示,怎么辦?那就擴充唄。
於是乎,誕生了ANSI編碼,這個編碼規定了如果第一個字節是0x80-0xFF開頭的話,就需要結合下一個字節來區分到底是什么字。擴充的字符的數量在128*256 = 32768 ,一下子就擴充了32768個字符,收錄了漢子,日本的,以及西歐各國的,事實上來說,比如中文,常用的中文也就4000個漢子左右,所有的中文數量加起來應該超過了80000個字,所以有些生僻的字使用ANSI編碼會出現亂碼(就是因為字符集不夠的原因造成的),而有些嵌入式系統,就是采用了這種編碼的機制。
下面就來舉舉例子吧 比如有個字符串 asidhi3535HI 轉換成 ANSI 的值 61 73 69 64 68 69 33 35 33 35 48 49 (和ASCII是一致的,所以算是最精簡的編碼了)
再比如有個字符串 asidhi3535HI是的 轉換成 ANSI 的值 61 73 69 64 68 69 33 35 33 35 48 49 CA C7 B5 C4 (也是相當的精簡,沒有任何多余的編碼),我們看到多了4個字節,那就是 CA C7 B5 C4 這個肯定是代表中文的 是的 的意思了。
但是ANSI在使用中有2個比較大問題:
- 字符處理的時候需要頻繁的判斷是否一個字節還是二個字節,嚴重的影響了性能,例如你在統計一串byte[]中的字符個數,那簡直是噩夢般的存在,早期的windows都是ansi編碼的,開發人員處理字符起來非常的痛苦。
- 個數有限,畢竟無法表示世界上全部的字符。
所以1988年由Apple及Xerox共同建立了unicode標准,在unicode的標准中,通常包含如下的標准 UTF-8,UTF-16,UTF-32,我們通常所有所說的unicode編碼就是UTF-16編碼。Unicode編碼由2個字節組成,不再將英文列為單個的字節了,這樣就大大的提高了字節的使用效率,但是最多仍然不能表示全世界的字節,那就啟用unicode的代理功能,對於那些實在是很偏僻的字使用4字節來表示。這樣,計算機可以達到性能和節省空間上達到一個比較均衡的點。
所以,在新一代的windows NT核心的開發上,全部換成了unicode編碼,對於C#開發,java開發都是采用unicode編碼。我們來舉例,還是上面的編碼
asidhi3535HI是的 轉換成ANSI編碼 61 73 69 64 68 69 33 35 33 35 48 49 CA C7 B5 C4
asidhi3535HI是的 轉換unicode編碼 61 00 73 00 69 00 64 00 68 00 69 00 33 00 35 00 33 00 35 00 48 00 49 00 2F 66 84 76 我們看到長度上來說比上述的ansi編碼要長很多,原因就是英文字符也采用了2個字節存儲。
仔細去分析,你很容易得出這樣的結論。unicode編碼對我們中國人來說,是非常友好的,我們的漢字都是2個字節,編碼已經是最高效了,但是對於美國人來說就不公平了,字符里帶了一大串無用的 00 ,浪費了存儲的硬盤和網絡傳送的帶寬,尤其是帶寬,因為編碼的問題,原本辦理100M的寬帶就夠了,現在卻要用200M,所以為了應對方便的存儲數據,方便在互聯上上網,加載頁面等等操作,都是使用UTF-8標准的編碼。
UTF-8 的字符編碼將一些編碼為1個字節,一些編碼為2個字節,一些編碼為3個字節,一些編碼4個字節,這樣就可以涵蓋全球所有的語言字符了(目前來說,並不是所有的字符都可以在計算機顯示,要讓計算機顯示出來,還有個必要的工作,制定字符集和字體,不然無法顯示,但是這個工作是個巨大的工程,所有有些國家的文字沒有出現在計算機中),所以UTF-8又叫萬國碼,這個編碼無論是對於美國來說,中國來說,至少在存儲和網絡傳輸上來說,是最省空間的。
至此,只剩最后一個編碼,叫UTF-32了,強制所有的字符都是4字節的,可以表示40億個字符,顯然也是萬國碼,但是缺點也是顯而易見的,巨大的浪費空間。所以只在特殊的情況下才會用到。
asidhi3535HI是的 轉換成UTF-32編碼 61 00 00 00 73 00 00 00 69 00 00 00 64 00 00 00 68 00 00 00 69 00 00 00 33 00 00 00 35 00 00 00 33 00 00 00 35 00 00 00 48 00 00 00 49 00 00 00 2F 66 00 00 84 76 00 00
接下來就是針對C#的解釋了,終於要進入正題了,講解C#的編碼及轉碼操作
首先第一個問題,為什么會存在編碼?
只要存在顯示和數據存儲就存在編碼!要不然沒有辦法顯示,你丟給屏幕一串61 73 69 64 68 69 33 35 33 35 48 49 CA C7 B5 C4 它怎么知道要顯示 asidhi3535HI是的 呢?
第二個問題,為什么存在亂碼?
如果你的文件存儲編碼是ANSI編碼,你卻用unicode去讀取,當然會位置錯亂。
用代碼來說就是
string str = "asidhi3535HI是的"; System.IO.File.WriteAllBytes( "123.txt", Encoding.Default.GetBytes( str ) ); // 這里的str2將會亂碼,不是 asidhi3535HI是的 string str2 = Encoding.Unicode.GetString( System.IO.File.ReadAllBytes( "123.txt" ) );
如果想要不亂碼,就需要前后的編碼對應才可以,同理,別人通過網絡給你發了一串字符串。也就是byte[],你在解析的時候一定需要知道里面的編碼是什么,這樣才能解析。
還有一個重點是什么。str的編碼是什么?之前說過C#都是unicode編碼的,對了,就是個。
為了說明編碼,再舉一個例子,就是上述自動生成的123.txt文本,里面的數據是asidhi3535HI是的,你現在要寫一個方法,判斷這個文件中是不是有“是的”這兩個中文字,
// 需要選擇指定的編碼解析 string str2 = Encoding.Default.GetString( System.IO.File.ReadAllBytes( "123.txt" ) ); if(str2.Contains("是的")) { // 包含 } else { // 沒有 }
到此為止,只要理解了編碼的原理,各個編碼之間的轉換也就很清晰明了了。今天的學習就到這里為止。