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