提及字符編碼首先可能想到的詞彙有Unicode,UTF,UCS,內碼,區位碼,跨平臺等詞彙,甚至還有輸入法,亂碼,emoji,微軟雅黑,URL encode等相關的詞彙也會冒出來,足以說明字符編碼在計算機中重要的地位。程序員關心的是在本身的代碼中如何處理好字符編碼的問題,特別是像C/C++這樣歷史悠久而又偏向底層的語言中如何處理好這一問題。實際上這個問題在今天已經獲得了很好的解決,前輩們經過努力使得今天的字符編碼基本上再也不困擾程序的使用者和編程人員,但其留下的諸多歷史痕跡卻還一直在困擾着一代又一代的編程初學者。本系列博客共有五篇,儘可能詳細但不失廣度,通俗不失深度地介紹關於字符編碼的內容,但求拋磚引玉。程序員
詳談字符編碼[一]將介紹編碼,輸入碼,機內碼,字形碼,字形庫等概念,主要說一下這些概念給咱們挖的坑。編程
接下來的幾篇還會介紹「什麼是代碼頁」,「字符輸出與消除亂碼」,「寬窄字符--字符相關的數據結構」,「經常使用IO函數的效率分析」等。windows
編碼(encode)的概念並不只僅出如今計算機科學中,字符編碼(character encoding)就是要找到一種可以用表示字符的方案,以方便存儲與傳輸。數組
電影《無間道》中梁朝偉和黃秋生經過傳輸莫斯電碼的方式傳輸信息,再或者小說裏兩情相悅的男女本身發明一套符號交流感情的橋段,這都是一種編碼。但咱們要談的主要仍是你們公認的字符的數字編碼。這些編碼像GBK,UTF-8,UCS-2,ASCII等等。還有Unicode?這就要說說這幾個概念的不一樣了。網絡
ASCII:信息技術萌發在西方社會,最先的編碼也只針對英語的習慣(ASCII只定義了英語字母和數字,符號等)。那時候總共也不超過128個字符,編碼方式一張表就全說明白了,表的內容正是字符與數字(0到127)的一一對應。在計算機上的具體實現方式就是用數字對應的二進制無符號數做爲字符的機器表示。數據結構
由於那時候要編碼的字符太少,人們甚至都沒怎麼感受到「規定字符和數字的對應方式」與「規定數字的二進制表示方式」是兩個相對獨立的過程。多年後,面對着浩如煙海的待編碼字符,這種分治成爲必要。函數
Unicode:Unicode一開始定位的問題就是給全世界的全部字符(包括古代字符)一個統一的身份證號。所謂統一就是誰跟誰也不會衝突,每一個字符都有本身的位子。相比給128個字符編碼,這個問題的複雜程度前無古人。分離「制定字符集」和「制定二進制編碼」這兩個過程勢在必行。字體
制定統一字符集就是給全世界的字符一個編號,稱爲碼點(code point ),通常用這樣「U+7231」(表示漢字 愛 )的方式表示。碼點是給人看的,機器只認識二進制,不認識什麼叫」U+7241「。因此字符集定義完成也只完成了一半工做,但已經完成了主要的工做。另外一部分的工做就是把數字定義爲二進制編碼,這時出現了多種方案,著名的就有UTF-8,UTF-16,UTF-32,UCS-2,UCS-4。編碼
字符集說明這種表示方式支持多少字符,好比Unicode能表示不少字符(並且仍在不斷擴充),ASCII就只有128個。而編碼方式則用來講明怎麼用二進制表示字符集裏字符對應的數字。到這裏應該明白,Unicode是指一個字符集,而UTF-8,UCS-2都是具體的編碼方式。ASCII呢,我想應該理解爲它既說明了字符集又說明了編碼方式。操作系統
分開」定義字符集「和」定義編碼「這兩部分的工做,好處有不少,我大概想到三點:
1.這兩部分工做用到的領域知識並不相同,所以自然須要分開。定義字符集的專家須要至關的社會學,語言學的人文知識,而編碼工做須要由有計算機底層知識的工程師去完成。
2.定義字符集的工做能夠不受編碼工做的影響,有利於字符集從此的擴充。Unicode的擴充一直在進行,但編碼方式基本只有UTF-8和UTF-16使用的最多,從此也基本不會再變。
3.不一樣的系統能夠選擇不一樣的二進制實現,哪一個方便選哪一個。不一樣的用途也能夠選擇不一樣的編碼,好比網絡傳輸用UTF-8,內部存儲用UTF-16。
網上已經有很多介紹具體編碼方式的文章,這裏再也不贅述,只畫一下重點。
Ⅰ。UTF-16與UCS-2是不一樣的編碼方式(UCS-4也不一樣與UTF-32)。簡單的來講,UCS是較舊的概念。早期Unicode認爲32bit足以表示全世界的字符,但現實是後來發現不夠便通過了一次擴充(U+XXXX變成U+XXXXXX,好比U+1F60A是emoji中的笑臉)。UCS-2只能表示擴充前的字符(請自行百度」Basic Multilingual Plane「)。UTF-16徹底兼容UCS-2,但支持全部Unicode字符,代價就是UTF-16是變長的,一個UTF-16字符多是16bit也可能用32bit(請自行百度」surrogate pair「)。
Ⅱ。UTF-16,UTF-32存在大小端的問題,UTF-8不存在。簡單來講由於UTF-16的碼元的2字節,UTF-32的碼元的4字節都是多字節,而UTF-8的碼元是一個字節,單字節不存在大小端問題(無所謂字節序)。
但真正要解釋清除還要說一下什麼是Unicode的碼元,碼元(是Unicode的碼元,不是通信學裏說的那個碼元)是機器一次處理的數據單位,是字節(8bit)的整數倍。要知道機器可不是一個字符一個字符處理的,而是一個碼元一個碼元的處理,具體來講C++中UTF-8不是用char數組存儲嗎,因此機器一次處理一個char類型(8bit)。若是碼元只包含一個字節,一次處理一個字節固然不會有大小端的問題。但若是是多字節碼元(UTF-16,UTF-32)就有區別了,x86是小端模式(x64不過是x86_64),他表示的UTF-16的’愛‘固然也應該等於7231可是在內存中就是按照0x3172這樣存儲的,不過,也不要緊畢竟機器本身認得在wchar_t的上下文中0x3172表示的值就是0x7231。問題是一旦換一臺新機器,鬼知道你的0x3172表示的是3172仍是7231?新機器也是小端那還好,默認認爲0x3172就是7231。但若是是大端的機器,默認理解3172就是3172,愛就變成另外一個字了。再看單字節碼元的UTF-8,不論在大端仍是小端的機器中都存儲爲E788B1,並且都理解爲E788B1,所以也就不存在大小端問題了。
Ⅲ。BOM(Byte Order Mark)是爲了區分大小端而存在的。用U+FEFF做爲第一個字符(選擇它是由於U+EFFF在是非法的碼點),U+FEFF這個碼點的名稱就是」零長不折行空格「,意思就是什麼都不顯示,零長度嘛,因此即使在字符中任意插入U+FEFF也不會影響顯示出來的結果。一臺機器讀取字符時發現前兩個字節是FFEF,就知道以後的文本是大端表示,若是前兩個字節是EFFF,就知道以後的文本是小端表示。UTF-8沒有大小端問題,BOM也就無關緊要,但若是有的話應該是什麼呢?就是U+FEFF採用UTF-8編碼獲得的:EFBBBF。
Ⅳ。知乎上有這樣的問題:」Windows爲何用GBK而不是UTF-8?「,「爲何Windows簡體中文默認編碼是GBK?」這可真冤枉微軟了,Windows會放着Unicode這麼好的東西不用嗎?其實Windows2000以後的操做系統內碼都是UTF-16了。若是你還有有這樣的誤會,請關注後續博客"什麼是代碼頁"。
這三個概念是應該放在一塊兒的,由於分別表明了一個字符從輸入處處理存儲再到輸出計算機的過程。輸入階段用到的是輸入碼,好比拼音碼,五筆字型碼,區位碼,他們基本上都對應了一類輸入法,用來完成字符的輸入。當咱們使用了五筆輸入並在按鍵上按下EP這兩個按鍵時,一系列按鍵消息傳到輸入法程序中,輸入法在把這一系列按鍵消息翻譯成咱們想輸入的漢字:愛。機內碼則是字符在計算機內的表示方式,通常說的字符編碼也是指機內碼。字形碼就有意思了。計算機知道內存中的一個字符是「愛」,要把它顯示到屏幕或打印到紙上,這就須要字形碼。他包含了一個字符形狀的點陣信息或者矢量信息,總之是和「字符的樣子」有關的信息。
圖1.1 漢字「愛」的16*16點陣
字形的信息存儲在字庫中(win10中C:/windows/Fonts/目錄下的就是關於字形的文件了)。好比上邊的「愛」字是一個能夠查詢字形信息的程序輸出的,這個程序就須要讀取附帶的字庫信息。單片機想要在一個LED顯示屏上顯示一個字符時用到的底層驅動程序也是相似原理,針式打印機要在紙上打印一樣須要字形信息。一個字固然能夠有不少種字體,每一種字體都須要對應字形庫的支持。多說一句字形設計但是有著做權保護的,方正楷體能夠免費商用,但微軟雅黑商用就須要付費了(但Windows用戶能夠在屏幕上顯示微軟雅黑,由於這部分版權費微軟已經給方正付過了)。
任何剛學完控制檯輸出的新手都有寫一個字符畫程序的衝動,這裏有一個之前寫的能夠將圖片轉換爲bmp位圖字符畫的程序例子,涉及字庫的使用,bmp位圖的文件格式,生成灰度圖等知識。(本身吐槽:這個程序很雞肋,要麼看得清圖片全貌看不清每一個漢字,要麼看得清漢字但圖片卻超出了屏幕範圍,要想寫一個更好的字符畫程序不得不研究一下圖像處理呢)這裏抖膽把效果展現一下。
原圖 縮小後的字符畫 字符畫局部,能夠看到由漢字組成
這篇的所有內容就是這樣了,下一篇會介紹」寬窄字符--字符相關的數據結構「,包括C++11中新加入的char16_t和char32_t的內容。喜歡就推薦一下吧。
祝你們國慶,中秋快樂!