徹底搞懂字符編碼ASCII,GB2312,UNICODE,UTF-8


閱讀了一篇關於編碼的博客(點擊打開鏈接)後,自己做了下總結,解釋一下基礎知識

基礎

能看到這篇文章, 我就假設你知道二進制,字節(byte),比特位(bit)這些概念了,如果不知道就先去了解下吧。

什麼是字符編碼?

我們都知道,計算機只能識別二進制,任何數據都是以二進制形式存儲在計算機上的,拿現實生活中的數字爲例,現實中的數字是十進制的,例如

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

在計算機上要保存爲二進制形式,我們假設計算機用一個字節(也就是8個bit(一個bit表示一個二進制位))表示十進制數字,那麼對應保存在計算機中的二進制就是(實際不是這麼保存的,這裏忽略了補碼,反碼,負數這些概念,因爲這不是我們要討論的重點):

0–>00000000

1–>00000001

2–>00000010

3–>00000011

4–>00000100

5–>00000101

6–>00000110

7–>00000111

8–>00001000

9–>00001001

10–>00001010

可以想到,我們用一個字節表示數字的話,一個字節共有2的8次方==256個狀態,只表示正整數的話,我們可以用一個字節表示 0-255這256個正整數,這些字節是順序存儲在計算機內存中的,例如計算機保存了這麼一段數據:

000010000000010100000111

計算機在識別的時候,會一次讀取一個字節,例如第一個字節是00001000,表示8,同理再讀取下一個字節,那麼上面這段數據就認爲這是857。好了,我們好像大概理解了數字是如何保存在計算機中並被計算機識別的了。

現實中除了123456這些數字,還有abcdef這些字符(以及中文字符,其他各國字符)需要計算機識別和表示,原理其實是差不多的。(字符的概念:簡單通俗理解,字符爲各國語言的最小單位,英語裏的“a”是一個字符,中文裏的"你"也是一個字符)

下面來理解一下字符編碼,就是字符如何保存在計算計中並被計算機識別,和數字類似,假設我們用一個字節表示字符,例如,

我們用00000000表示a,用00000001表示b等等,計算機會一個字節一個字節的讀取,讀到00000000就知道這是a了,同理,一個字節有256種狀態,那麼一個字節就可以表示256個字符,這種把字符和相應的二進制一一對應起來,從而讓計算機可以識別字符的過程,就是字符編碼(實際計算機中肯定不會00000000對應a,00000001對應b這麼簡單的,這也是我們接下來要討論的),可以想到,一個字節這肯定是不夠的,我們有多少中文啊,其他國家還有多少字符呢。

正文

ASCII

就像基礎中介紹的,ASCII是用7位bit表示字符的,所以只可以表示128個字符,一開始美國人覺得128個字符夠用了。

ASCII擴展碼

後來美國人發現128個字符不夠用,就用8個bit(也就是一個字節)表示一個字符,那麼可以表示256個字符了。

其中前128個和ASCII一模一樣,後面增加128個新的字符而已,所以才叫ASCII擴展碼,主要表示英文字符和一些打印字符,非打印字符等。

中國人說話了,一個自己最多256種狀態,漢字遠遠不夠表示啊。

GB2312

是對 ASCII 的中文擴展(注意是在ASCII碼的基礎上進行中文擴展,而不是在ASCII擴展碼基礎上)。也是直接存儲方式。

規定把那些127號之後的奇異符號們直接取消掉(也就是取消擴展的ASCII碼), 規定:一個小於127的字符的意義與原來相同,但兩個大於127的字符連在一起時,就表示一個漢字,前面的一個字節(他稱之爲高字節)從0xA1用到 0xF7,後面一個字節(低字節)從0xA1到0xFE,這樣我們就可以組合出大約7000多個簡體漢字了。在這些編碼裏,我們還把數學符號、羅馬希臘的字母、日文的假名們都編進去了,連在 ASCII 裏本來就有的數字、標點、字母都統統重新編了兩個字節長的編碼,這就是常說的”全角”字符,而原來在127號以下的那些就叫”半角”字符了。 中國人民看到這樣很不錯,於是就把這種漢字方案叫做 “GB2312“。

GBK

把 GB2312 沒有用到的碼位找出來老實不客氣地用上。 後來還是不夠用,於是乾脆不再要求低字節一定是127號之後的內碼,只要第一個字節是大於127就固定表示這是一個漢字的開始,不管後面跟的是不是擴展字符集裏的內容。結果擴展之後的編碼方案被稱爲 GBK 標準。

DBCS

中國的程序員們看到這一系列漢字編碼的標準是好的,於是通稱他們叫做 “DBCS“(Double Byte Charecter Set 雙字節字符集)。在DBCS系列標準裏,最大的特點是兩字節長的漢字字符和一字節長的英文字符並存於同一套編碼方案裏
舉個例子解釋兩字節長的漢字字符和一字節長的英文字符並存於同一套編碼方案裏:

爲了便於理解,我們用十進制解釋,我們把a,b,c,d,e,f這6個英文字符,和“漢“,字”這兩個字符用十進制編碼,英文用一位十進制表示,中文用2位十進制表示:規定用單個的0-5表示英文a-e,大於5的數字,和下一位一起表示一個漢字,例如50表示“漢”,51表示“字”。那麼1513350則表示“b字dd漢”,這就相當於把兩位的漢字,和一位的英文共存於同一套編碼方案。

ASCII系列編碼是直接存儲的,也就是說規定哪種字符對應哪種二進制,就把這種二進制原樣存儲在內存中。例如,假設編碼規定00000000表示a,那麼a保存在內存中就是00000000。後面會介紹UNICODE幾種非直接存儲(編碼存儲(UTF系列))

看到這,你也應該理解了,在ascii編碼系列中程序員們常常唸叨的一個英文字符是一個字節,一箇中文字符是兩個字節的含義了。

ascii編碼系列最多用兩個字節,最多也就表示2的16次方==65535個字符,加上其他國家的字符,還不夠,怎麼辦?

UNICODE

Unicode也是一種字符編碼方法, 不過它是由國際組織設計, 可以容納全世界所有語言文字的編碼方案。相當於爲世界上每種字符都定義了一個數字來表示。
但是UNICODE雖然給每個字符都定義了一個數字表示,但是直接存儲的話可能需要大於1個字節(例如假設字符a用256來表示,則存儲在內存中爲11111111,它是8位bit,佔用了一個字節;假設字符b用257表示,則只能用兩個字節表示000000100000000,同理,假如字符x用389表示呢,所以直接存儲其二進制可能會造成長短不一),要將它按照字節存儲,就有兩個問題:

  1. 如何區分單獨1個字節表示一個字符還是2個字節表示一個字符還是3個字節(注:ascii系列編碼中是通過第一個字節是不是大於127來區分的)

  2. 那麼爲了方便區分, 如果unicode統一規定, 每個符號用三個或四個字節表示, 那麼每個英文字母前都必然有二到三個字節是0, 這對於存儲來說是極大的浪費,因爲英文字母只用一個字節表示就夠了(例如統一規定所有的字符都用三個字節表示那麼字符a可能表示爲000000000000000000000001,顯然相比ansii編碼多出了前兩個字節的0很浪費),文本文件的大小會因此大出二三倍, 這是無法接受的

爲了解決這兩個問題,出現了UNICODE的不同編碼方式,這些編碼方式相當於規定了UNICODE碼的存儲規則,來解決上面兩個問題,也就是說想辦法既能很好的區分出不同的二進制表示的含義,又不那麼浪費存儲空間,所以需要存儲UNICODE前,經過一些轉換再存儲,這就是UTF(Unicode Transformation Format:unicode轉換格式)系列編碼。

UTF-8

通過前導字節的方式(給每個字節加10標誌位)來存儲,既能很好的區分出不同的二進制表示的含義,又節省存儲空間。是一種針對Unicode的可變長度字符編碼,UTF-8用1到6個字節編碼Unicode字符(所以對於UTF-8,一個字符是幾個字節就不確定了,有可能是1個,2個或者3個甚至是6個)

UTF-16(USC-2)

定死兩個字節表示一個字符(顯然對於英文字符會有一個字節的浪費),通常說的UNICODE就是指的UTF-16,以小端方式存儲。(對於UTF-16,無論是中文還是英文,一個字符都是兩個字節)

UTF-32(USC-4)

定死四個字節表示一個字符,相比UTF-16是範圍更廣了,表示的字符更多了,同時空間浪費也更加嚴重

注:關於utf-8的具體如何編碼的,可以自行查看相關文檔。

編程語言對字符編碼的支持

總結一下上面的字符編碼:

  • ASCII編碼的字符只需要一個字節就能存儲

  • GB2312,GBK等DBCS系列需要一個或兩個字節存儲

  • UTF-8需要1-6個字節存儲

  • UTF-16需要2個字節存儲,windows中的UNICODE編碼就是用UTF-16存儲的,所以在VC編程中,一說UNICODE就是指UTF-16

這些需要不同存儲空間的字符編碼,該如何保存在程序中呢,畢竟c語言一開始只有char類型(一個字節)來表示字符,聰明的人類總是有辦法,把字符編碼分類:

  • ASCII,DBCS系列,utf-8等需要一個或多個存儲空間的編碼表示的字符串叫多字節編碼字符串,用char數組保存。

  • UTF-16,也就是UNICODE,需要固定2個字節空間的編碼表示的字符串叫寬字節編碼字符串,用w_char數組保存,一個w_char兩個字節(平臺相關,在win32下w_char兩個字節,在linux下wi_char是四個字節)。

有了多字節和寬字節的分類後,所有的字符串相關的操作,都有兩套API分別針對多字節和寬字節,例如計算字符串長度的函數,對於多字節字符串用strlen計算,對於寬字節字符串用wcslen計算。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章