W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
在計(jì)算機(jī)中,所有數(shù)據(jù)都是以二進(jìn)制數(shù)的形式存儲(chǔ)的,字符 char 也不例外。為了表示字符,我們需要建立一套“字符集”,規(guī)定每個(gè)字符和二進(jìn)制數(shù)之間的一一對(duì)應(yīng)關(guān)系。有了字符集之后,計(jì)算機(jī)就可以通過查表完成二進(jìn)制數(shù)到字符的轉(zhuǎn)換。
「ASCII 碼」是最早出現(xiàn)的字符集,全稱為“美國(guó)標(biāo)準(zhǔn)信息交換代碼”。它使用 7 位二進(jìn)制數(shù)(即一個(gè)字節(jié)的低 7 位)表示一個(gè)字符,最多能夠表示 128 個(gè)不同的字符。如圖 3-6 所示,ASCII 碼包括英文字母的大小寫、數(shù)字 0 ~ 9、一些標(biāo)點(diǎn)符號(hào),以及一些控制字符(如換行符和制表符)。
圖 3-6 ASCII 碼
然而,ASCII 碼僅能夠表示英文。隨著計(jì)算機(jī)的全球化,誕生了一種能夠表示更多語言的字符集「EASCII」。它在 ASCII 的 7 位基礎(chǔ)上擴(kuò)展到 8 位,能夠表示 256 個(gè)不同的字符。
在世界范圍內(nèi),陸續(xù)出現(xiàn)了一批適用于不同地區(qū)的 EASCII 字符集。這些字符集的前 128 個(gè)字符統(tǒng)一為 ASCII 碼,后 128 個(gè)字符定義不同,以適應(yīng)不同語言的需求。
后來人們發(fā)現(xiàn),EASCII 碼仍然無法滿足許多語言的字符數(shù)量要求。比如漢字大約有近十萬個(gè),光日常使用的就有幾千個(gè)。中國(guó)國(guó)家標(biāo)準(zhǔn)總局于 1980 年發(fā)布了「GB2312」字符集,其收錄了 6763 個(gè)漢字,基本滿足了漢字的計(jì)算機(jī)處理需要。
然而,GB2312 無法處理部分的罕見字和繁體字?!窯BK」字符集是在 GB2312 的基礎(chǔ)上擴(kuò)展得到的,它共收錄了 21886 個(gè)漢字。在 GBK 的編碼方案中,ASCII 字符使用一個(gè)字節(jié)表示,漢字使用兩個(gè)字節(jié)表示。
隨著計(jì)算機(jī)的蓬勃發(fā)展,字符集與編碼標(biāo)準(zhǔn)百花齊放,而這帶來了許多問題。一方面,這些字符集一般只定義了特定語言的字符,無法在多語言環(huán)境下正常工作。另一方面,同一種語言也存在多種字符集標(biāo)準(zhǔn),如果兩臺(tái)電腦安裝的是不同的編碼標(biāo)準(zhǔn),則在信息傳遞時(shí)就會(huì)出現(xiàn)亂碼。
那個(gè)時(shí)代的研究人員就在想:如果推出一個(gè)足夠完整的字符集,將世界范圍內(nèi)的所有語言和符號(hào)都收錄其中,不就可以解決跨語言環(huán)境和亂碼問題了嗎?在這種想法的驅(qū)動(dòng)下,一個(gè)大而全的字符集 Unicode 應(yīng)運(yùn)而生。
「Unicode」的全稱為“統(tǒng)一字符編碼”,理論上能容納一百多萬個(gè)字符。它致力于將全球范圍內(nèi)的字符納入到統(tǒng)一的字符集之中,提供一種通用的字符集來處理和顯示各種語言文字,減少因?yàn)榫幋a標(biāo)準(zhǔn)不同而產(chǎn)生的亂碼問題。
自 1991 年發(fā)布以來,Unicode 不斷擴(kuò)充新的語言與字符。截止 2022 年 9 月,Unicode 已經(jīng)包含 149186 個(gè)字符,包括各種語言的字符、符號(hào)、甚至是表情符號(hào)等。在龐大的 Unicode 字符集中,常用的字符占用 2 字節(jié),有些生僻的字符占 3 字節(jié)甚至 4 字節(jié)。
Unicode 是一種字符集標(biāo)準(zhǔn),本質(zhì)上是給每個(gè)字符分配一個(gè)編號(hào)(稱為“碼點(diǎn)”),但它并沒有規(guī)定在計(jì)算機(jī)中如何存儲(chǔ)這些字符碼點(diǎn)。我們不禁會(huì)問:當(dāng)多種長(zhǎng)度的 Unicode 碼點(diǎn)同時(shí)出現(xiàn)在同一個(gè)文本中時(shí),系統(tǒng)如何解析字符?例如給定一個(gè)長(zhǎng)度為 2 字節(jié)的編碼,系統(tǒng)如何確認(rèn)它是一個(gè) 2 字節(jié)的字符還是兩個(gè) 1 字節(jié)的字符?
對(duì)于以上問題,一種直接的解決方案是將所有字符存儲(chǔ)為等長(zhǎng)的編碼。如圖 3-7 所示,“Hello”中的每個(gè)字符占用 1 字節(jié),“算法”中的每個(gè)字符占用 2 字節(jié)。我們可以通過高位填 0 ,將“Hello 算法”中的所有字符都編碼為 2 字節(jié)長(zhǎng)度。這樣系統(tǒng)就可以每隔 2 字節(jié)解析一個(gè)字符,恢復(fù)出這個(gè)短語的內(nèi)容了。
圖 3-7 Unicode 編碼示例
然而 ASCII 碼已經(jīng)向我們證明,編碼英文只需要 1 字節(jié)。若采用上述方案,英文文本占用空間的大小將會(huì)是 ASCII 編碼下大小的兩倍,非常浪費(fèi)內(nèi)存空間。因此,我們需要一種更加高效的 Unicode 編碼方法。
目前,UTF-8 已成為國(guó)際上使用最廣泛的 Unicode 編碼方法。它是一種可變長(zhǎng)的編碼,使用 1 到 4 個(gè)字節(jié)來表示一個(gè)字符,根據(jù)字符的復(fù)雜性而變。ASCII 字符只需要 1 個(gè)字節(jié),拉丁字母和希臘字母需要 2 個(gè)字節(jié),常用的中文字符需要 3 個(gè)字節(jié),其他的一些生僻字符需要 4 個(gè)字節(jié)。
UTF-8 的編碼規(guī)則并不復(fù)雜,分為以下兩種情況。
圖 3-8 展示了“Hello算法”對(duì)應(yīng)的 UTF-8 編碼。觀察發(fā)現(xiàn),由于最高 n 位都被設(shè)置為 1 ,因此系統(tǒng)可以通過讀取最高位 1 的個(gè)數(shù)來解析出字符的長(zhǎng)度為 n 。
但為什么要將其余所有字節(jié)的高 2 位都設(shè)置為 10 呢?實(shí)際上,這個(gè) 10 能夠起到校驗(yàn)符的作用。假設(shè)系統(tǒng)從一個(gè)錯(cuò)誤的字節(jié)開始解析文本,字節(jié)頭部的 10 能夠幫助系統(tǒng)快速的判斷出異常。
之所以將 10 當(dāng)作校驗(yàn)符,是因?yàn)樵?UTF-8 編碼規(guī)則下,不可能有字符的最高兩位是 10 。這個(gè)結(jié)論可以用反證法來證明:假設(shè)一個(gè)字符的最高兩位是 10 ,說明該字符的長(zhǎng)度為 1 ,對(duì)應(yīng) ASCII 碼。而 ASCII 碼的最高位應(yīng)該是 0 ,與假設(shè)矛盾。
圖 3-8 UTF-8 編碼示例
除了 UTF-8 之外,常見的編碼方式還包括以下兩種。
從存儲(chǔ)空間的角度看,使用 UTF-8 表示英文字符非常高效,因?yàn)樗鼉H需 1 個(gè)字節(jié);使用 UTF-16 編碼某些非英文字符(例如中文)會(huì)更加高效,因?yàn)樗恍枰?2 個(gè)字節(jié),而 UTF-8 可能需要 3 個(gè)字節(jié)。
從兼容性的角度看,UTF-8 的通用性最佳,許多工具和庫(kù)都優(yōu)先支持 UTF-8 。
對(duì)于以往的大多數(shù)編程語言,程序運(yùn)行中的字符串都采用 UTF-16 或 UTF-32 這類等長(zhǎng)的編碼。在等長(zhǎng)編碼下,我們可以將字符串看作數(shù)組來處理,這種做法具有以下優(yōu)點(diǎn)。
實(shí)際上,編程語言的字符編碼方案設(shè)計(jì)是一個(gè)很有趣的話題,其涉及到許多因素。
String
? 類型使用 UTF-16 編碼,每個(gè)字符占用 2 字節(jié)。這是因?yàn)?Java 語言設(shè)計(jì)之初,人們認(rèn)為 16 位足以表示所有可能的字符。然而,這是一個(gè)不正確的判斷。后來 Unicode 規(guī)范擴(kuò)展到了超過 16 位,所以 Java 中的字符現(xiàn)在可能由一對(duì) 16 位的值(稱為“代理對(duì)”)表示。由于以上編程語言對(duì)字符數(shù)量的低估,它們不得不采取“代理對(duì)”的方式來表示超過 16 位長(zhǎng)度的 Unicode 字符。這是一個(gè)不得已為之的無奈之舉。一方面,包含代理對(duì)的字符串中,一個(gè)字符可能占用 2 字節(jié)或 4 字節(jié),從而喪失了等長(zhǎng)編碼的優(yōu)勢(shì)。另一方面,處理代理對(duì)需要增加額外代碼,這增加了編程的復(fù)雜性和 Debug 難度。
出于以上原因,部分編程語言提出了一些不同的編碼方案。
需要注意的是,以上討論的都是字符串在編程語言中的存儲(chǔ)方式,這和字符串如何在文件中存儲(chǔ)或在網(wǎng)絡(luò)中傳輸是兩個(gè)不同的問題。在文件存儲(chǔ)或網(wǎng)絡(luò)傳輸中,我們通常會(huì)將字符串編碼為 UTF-8 格式,以達(dá)到最優(yōu)的兼容性和空間效率。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: