天天看點

ASCII,GB2312,UNICODE,UTF-8總結基礎正文程式設計語言對字元編碼的支援

閱讀了一篇關于編碼的部落格(點選打開連結)後,自己做了下總結,解釋一下基礎知識

基礎

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

1. 什麼是字元編碼?

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

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兩個位元組。

有了多位元組和寬位元組的分類後,所有的字元串相關的操作,都有兩套API分别針對多位元組和寬位元組,例如計算字元串長度的函數,對于多位元組字元串用strlen計算,對于寬位元組字元串用wcslen計算。