轉(zhuǎn)自:https://cloud.tencent.com/developer/article/1470820
字符編碼
你是否認(rèn)為 ASCII 碼就是一個(gè)字符,一個(gè)字節(jié)就是一個(gè)字符,一個(gè)字符就是 8 比特?你是否認(rèn)為 UTF-8 就是用 8 比特表示一個(gè)字符葵擎?如果真的是這樣認(rèn)為這篇文章就很適合你蜡豹。
為什么要有編碼麸粮?
首先大家需要明確的是在計(jì)算機(jī)里所有的數(shù)據(jù)都是字節(jié)的形式存儲(chǔ)和處理的。我們需要字節(jié)來(lái)表示計(jì)算機(jī)里的信息镜廉,但是這些字節(jié)本身又是沒(méi)有任何意義的弄诲。我們需要對(duì)這些字節(jié)賦予實(shí)際的意義,制定各種編碼標(biāo)準(zhǔn)娇唯。
編碼模型
首先需要知道的是存在兩種編碼模型
簡(jiǎn)單字符集
在這種編碼模型里齐遵,一個(gè)字符集定義了這個(gè)字符集里包含什么字符,同時(shí)把每個(gè)字符如何對(duì)應(yīng)成計(jì)算機(jī)里的比特也進(jìn)行了定義塔插。例如 ASCII梗摇,在 ASCII 里直接定義了 A -> 0100 0001
。
現(xiàn)代編碼模型
在現(xiàn)代編碼模型里要知道一個(gè)字符如何映射成計(jì)算機(jī)里比特想许,需要經(jīng)過(guò)如下幾個(gè)步驟:
知道一個(gè)系統(tǒng)需要支持哪些字符伶授,這些字符的集合被稱為字符表(Character repertoire)
給字符表里的抽象字符編上一個(gè)數(shù)字,也就是字符集合到一個(gè)整數(shù)集合的映射伸刃。這種映射稱為編碼字符集(CCS:Coded Character Set), unicode 是屬于這一層的概念谎砾,unicode 跟計(jì)算機(jī)里的什么進(jìn)制啊沒(méi)有任何關(guān)系,它是完全數(shù)學(xué)的抽象的捧颅。
將 CCS 里字符對(duì)應(yīng)的整數(shù)轉(zhuǎn)換成有限長(zhǎng)度的比特值景图,便于以后計(jì)算機(jī)使用一定長(zhǎng)度的二進(jìn)制形式表示該整數(shù)。這個(gè)對(duì)應(yīng)關(guān)系被稱為字符編碼表(CEF:Character Encoding Form)UTF-8, UTF-16 都屬于這層碉哑。
對(duì)于 CEF 得到的比特值具體如何在計(jì)算機(jī)中進(jìn)行存儲(chǔ)挚币,傳輸亮蒋。因?yàn)榇嬖诖蠖诵《说膯?wèn)題,這就會(huì)跟具體的操作系統(tǒng)相關(guān)了妆毕。這種解決方案稱為字符編碼方案(CES:Character Encoding Scheme)慎玖。
平常我們所說(shuō)的編碼都在第三步的時(shí)候完成了,并沒(méi)有涉及到 CES。所以 CES 并不在本文的討論范圍之內(nèi)笛粘。 現(xiàn)在也許有人會(huì)想為什么要有現(xiàn)代的編碼模型趁怔?為什么在現(xiàn)在的編碼模型要拆分出這么多概念?直接像原始的編碼模型直接都規(guī)定好所有的信息不行嗎薪前?這些問(wèn)題在下文的編碼發(fā)展史中都會(huì)有所闡述润努。
編碼的發(fā)展史
ASCII
ASCII 出現(xiàn)在上個(gè)世紀(jì) 60 年代的美國(guó),ASCII 一共定義了 128 個(gè)字符示括,使用了一個(gè)字節(jié)的 7 位铺浇。定義的這些字符包括英文字母 A-Z,a-z垛膝,數(shù)字 0-9鳍侣,一些標(biāo)點(diǎn)符號(hào)和控制符號(hào)。在 Shell 里輸入man ASCII
吼拥,可以看到完整的 ASCII 字符集倚聚。ASCII 采用的編碼模型是簡(jiǎn)單字符集,它直接定義了一個(gè)字符的比特值表示凿可。例如上文提到的A -> 0100 0001
秉沼。也就是 ASCII 直接完成了現(xiàn)代編碼模型的前三步工作。 在英語(yǔ)系國(guó)家里 ASCII 標(biāo)準(zhǔn)很完美矿酵。但是不要忘了世界上可有好幾千種語(yǔ)言,這些語(yǔ)言里不僅只有這些符號(hào)啊矗积。如果使用這些語(yǔ)言的人也想使用計(jì)算機(jī)全肮,ASCII 就遠(yuǎn)遠(yuǎn)不夠了。所以到這里編碼進(jìn)入了混亂的時(shí)代棘捣。
混亂時(shí)代
人們知道計(jì)算機(jī)的一個(gè)字節(jié)是 8 位辜腺,可以表示 256 個(gè)字符。ASCII 卻只使用了 7 位乍恐,所以人們決定把剩余的一位也利用起來(lái)评疗。這時(shí)問(wèn)題出現(xiàn)了,人們對(duì)于已經(jīng)規(guī)定好的 128 個(gè)字符是沒(méi)有異議的茵烈,但是不同語(yǔ)系的人對(duì)于其他字符的需求是不一樣的百匆,所以對(duì)于剩下的 128 個(gè)字符的擴(kuò)展會(huì)千奇百怪。而且更加混亂的是呜投,在亞洲的語(yǔ)言系統(tǒng)中有更多的字符加匈,一個(gè)字節(jié)無(wú)論如何也滿足不了需求了存璃。例如僅漢字就有 10 萬(wàn)多個(gè),一個(gè)字節(jié)的 256 表示方式怎么能夠滿足呢雕拼。于是就又產(chǎn)生了各種多字節(jié)的表示一個(gè)字符方法(gbk 就是其中一種)纵东,這就使整個(gè)局面更加的混亂不堪。(希望看到這里的你不再認(rèn)為一個(gè)字節(jié)就是一個(gè)字符啥寇,一個(gè)字符就是8比特)偎球。每個(gè)語(yǔ)系都有自己特定的編碼頁(yè)(code pages)的狀況,使得不同的語(yǔ)言出現(xiàn)在同一臺(tái)計(jì)算機(jī)上辑甜,不同語(yǔ)系的人在網(wǎng)絡(luò)上進(jìn)行交流都成了癡人說(shuō)夢(mèng)衰絮。這時(shí) Unicode 出現(xiàn)了。
Unicode
Unicode 就是給計(jì)算機(jī)中所有的字符各自分配一個(gè)代號(hào)栈戳。Unicode 通俗來(lái)說(shuō)是什么呢岂傲?就是現(xiàn)在實(shí)現(xiàn)共產(chǎn)主義了,各國(guó)人民不在需要自己特定的國(guó)家身份證子檀,而是給每人一張全世界通用的身份證镊掖。Unicode 是屬于編碼字符集(CCS)的范圍。Unicode 所做的事情就是將我們需要表示的字符表中的每個(gè)字符映射成一個(gè)數(shù)字褂痰,這個(gè)數(shù)字被稱為相應(yīng)字符的碼點(diǎn)(code point)亩进。例如“嚴(yán)”字在 Unicode 中對(duì)應(yīng)的碼點(diǎn)是 U+0x4E25。
到目前為止缩歪,我們只是找到了一堆字符和數(shù)字之間的映射關(guān)系而已归薛,只到了CCS的層次。這些數(shù)字如何在計(jì)算機(jī)和網(wǎng)絡(luò)中存儲(chǔ)和展示還沒(méi)有提到匪蝙。
字符編碼
前面還都屬于字符集的概念主籍,現(xiàn)在終于到 CEF 的層次了。為了便于計(jì)算的存儲(chǔ)和處理逛球,現(xiàn)在我們要把哪些純數(shù)學(xué)數(shù)字對(duì)應(yīng)成有限長(zhǎng)度的比特值了千元。最直觀的設(shè)計(jì)當(dāng)然是一個(gè)字符的碼點(diǎn)是什么數(shù)字,我們就把這個(gè)數(shù)字轉(zhuǎn)換成相應(yīng)的二進(jìn)制表示颤绕,例如“嚴(yán)”在 Unicode 中對(duì)應(yīng)的數(shù)字是 0x4E25,他的二進(jìn)制是100 1110 0010 0101
幸海,也就是嚴(yán)這個(gè)字需要兩個(gè)字節(jié)進(jìn)行存儲(chǔ)。按照這種方法大部分漢字都可以用兩個(gè)字節(jié)來(lái)表示了奥务。但是還有其他語(yǔ)系的存在物独,沒(méi)準(zhǔn)兒他們所使用的字符用這種方法轉(zhuǎn)換就需要 4 個(gè)字節(jié)。這樣問(wèn)題又來(lái)了到底該使用幾個(gè)字節(jié)表示一個(gè)字符呢氯葬?如果規(guī)定兩個(gè)字節(jié)挡篓,有的字符會(huì)表示不出來(lái),如果規(guī)定較多的字節(jié)表示一個(gè)字符帚称,很多人又不答應(yīng)瞻凤,因?yàn)楸緛?lái)有些語(yǔ)言的字符兩個(gè)字節(jié)處理就可以了憨攒,憑什么用更多的字節(jié)表示,多么浪費(fèi)阀参。
這時(shí)就會(huì)想可不可以用變長(zhǎng)的字節(jié)來(lái)存儲(chǔ)一個(gè)字符呢肝集?如果使用了變長(zhǎng)的字節(jié)表示一個(gè)字符,那就必須要知道是幾個(gè)字節(jié)表示了一個(gè)字符蛛壳,要不然計(jì)算機(jī)可沒(méi)那么聰明杏瞻。下面介紹一下最常用的 UTF-8(UTF 是Unicode Transformation Format的縮寫(xiě))的設(shè)計(jì)。請(qǐng)看下圖(來(lái)自阮一峰的博客衙荐,博客地址:https://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html)
其中:x 表示可用的位
通過(guò) UTF-8 的對(duì)應(yīng)關(guān)系可以把每個(gè)字符在Unicode 中對(duì)應(yīng)的碼點(diǎn)捞挥,轉(zhuǎn)換成相應(yīng)的計(jì)算機(jī)的二進(jìn)制表示∮且鳎可以發(fā)現(xiàn)按照 UTF-8 進(jìn)行轉(zhuǎn)換是完全兼容原先的 ASCII 的砌函;而且在多字節(jié)表示一個(gè)字符時(shí),開(kāi)頭有幾個(gè) 1 就表示這個(gè)字符按照 UTF-8 轉(zhuǎn)換后由幾個(gè)字節(jié)表示溜族。下面一個(gè)實(shí)例子來(lái)自阮一峰的博客讹俊。
已知“嚴(yán)”的unicode是4E25(100111000100101),根據(jù)上表煌抒,可以發(fā)現(xiàn)4E25處在第三行的范圍內(nèi)(0000 0800-0000 FFFF)仍劈,因此“嚴(yán)”的UTF-8編碼需要三個(gè)字節(jié),即格式是“1110xxxx 10xxxxxx 10xxxxxx”寡壮。然后贩疙,從“嚴(yán)”的最后一個(gè)二進(jìn)制位開(kāi)始,依次從后向前填入格式中的x况既,多出的位補(bǔ)0这溅。這樣就得到了,“嚴(yán)”的UTF-8編碼是“11100100 10111000 10100101”棒仍,轉(zhuǎn)換成十六進(jìn)制就是0xE4B8A5芍躏。
注:【依次從后向前填入格式中的x】意思是,將“嚴(yán)”的二進(jìn)制表示從后往前降狠,依次替代 x
除了 UTF-8 這種轉(zhuǎn)換方法,還存在 UTF-16庇楞,UTF-32 等等轉(zhuǎn)換方法榜配。這里就不再多做介紹。(注意UTF后邊的數(shù)字代表的是碼元的大小吕晌。碼元(Code Unit)是指一個(gè)已編碼的文本中具有最短的比特組合的單元蛋褥。對(duì)于 UTF-8 來(lái)說(shuō),碼元是 8 比特長(zhǎng)睛驳;對(duì)于 UTF-16 來(lái)說(shuō)烙心,碼元是 16 比特長(zhǎng)膜廊。換一種說(shuō)法就是 UTF-8 的是以一個(gè)字節(jié)為最小單位的,UTF-16 是以兩個(gè)字節(jié)為最小單位的淫茵。)