JavaScript開發(fā)中需要搞懂的字符編碼總結(jié)
目錄
- 字符集和字符編碼
- ASCII
- 字符集的發(fā)展歷史
- Unicode
- UTF-8
- UTF-16
- 比較
- 前端開發(fā)中的編碼
- 字符串長度計算
- 組合字符的長度
- 多碼元字符操作
- 正則中的 u 修飾符
- 轉(zhuǎn)義字符
- 常用API
- 處理碼點和字符
- TextEncoder
- String.prototype.normalize()
- URL的UTF8編解碼
字符集和字符編碼
字符集就是字符的集合,如常見的 ASCII字符集,GB2312字符集,Unicode字符集等。這些不同字符集之間最大的區(qū)別是所包含的字符數(shù)量的不同。
字符編碼則代表字符集的實際編碼規(guī)則,是用于計算機解析字符的,如 GB2312,GBK,UTF-8 等。字符編碼的本質(zhì)就是如何使用二進制字節(jié)來表示字符的問題。
字符集和編碼是一對多的關(guān)系,同一字符集可能有多種字符編碼,如Unicode字符集就有 UTF-8,UTF-16 等。
在前端開發(fā)中,Javascript程序是使用Unicode字符集,Javascript源碼文本通常是基于UTF-8編碼。
但JS代碼中的字符串類型是UTF-16編碼的,這也是為什么會碰到api接口返回字符串在前端出現(xiàn)亂碼,因為多數(shù)后臺服務(wù)都使用utf-8編碼,前后編碼方式不一致。
說起字符集的發(fā)展歷程,可以總結(jié)為一句話:幾乎都是對ASCII字符集的擴展。
ASCII
我們知道,計算機是使用二進制來處理信息的。
其中,每一個二進制位(bit)有 0和1 兩種狀態(tài)。一個字節(jié)(byte)則有8個二進制位,可以有256種狀態(tài)。
而ASCII就是基于拉丁字母、主要用于顯示英文的一種單字節(jié)字符集,它的編碼和字符是一一對應(yīng)的,因為它就是使用一個字節(jié)8個二進制位來表示,不會超過256個字符。
標(biāo)準(zhǔn)的ASCII字符總計有128個字符(2^7),其中前面32個控制字符,后面96個是可打印字符,包括常用的大小寫字母數(shù)字標(biāo)點符號等。因為只占用了一個字節(jié)的后7位,那字節(jié)的最高位一般設(shè)置為0。
"a".charCodeAt() // 97 "A".charCodeAt() // 65 "9".charCodeAt() // 57 ".".charCodeAt() // 46
如上,每個字符會對應(yīng)一個編碼(使用數(shù)字標(biāo)識),總共會從0-128。完整的ASCII碼表,網(wǎng)上很容易找到。
通過ASCII碼表,我們發(fā)現(xiàn),小寫字母并沒有和大寫字母挨著排序?
這是為了方便大小寫之間的轉(zhuǎn)換, A 排在 65(64 + 1) 位,而 a 排在 97(64 + 32 + 1) 位。
65 ^ 32 = 97 // A ^ 32 = a
字符集的發(fā)展歷史
ASCII是幾乎所有字符集的基礎(chǔ)。
標(biāo)準(zhǔn)的ASCII碼最多只能標(biāo)識128個字符,歐美國家可以很好的使用,但其他國家的字符變多,自然就不夠用了。
這個時候,最高位就開始被惦記上,通過擴展ASCII碼的最高位,又能滿足用于特殊符號的一些國家的需求,這種就是擴展ASCII碼。
但是亞非拉更多非拉丁語系的國家,字符成千上萬,只能使用新的方式。
如中文,就又進行了擴展,小于127的字符的意義與標(biāo)準(zhǔn)ASCII碼相同,當(dāng)需要標(biāo)識漢字時,使用2個字節(jié),每個字節(jié)都大于127。這種多字節(jié)字符集即GB2312,后續(xù)因為不斷的擴展,如繁體字和各種符號,甚至少數(shù)民族的語言符號等等,又使用了包括GBK等不同字符集。
因此,很多國家都制定了自己的編碼字符集,基本都是在ASCII的基礎(chǔ)上進行的。
各字符集雖然都能夠兼容標(biāo)準(zhǔn)ASCII碼,但在使用交流上的不便是顯而易見的,亂碼也是隨處可見。為了解決這種各自為戰(zhàn)的問題,Unicode字符集就誕生了。
Unicode
Unicode
是國際組織制定的,用于收納世界上所有文字和符號的字符集方案。
前128個字符同ASCII一樣,進行擴充后,使用數(shù)字0-0x10FFFF來映射這些字符,最多可以有1114112個字符。目前仍然只使用了其中的一小部分。
Unicode一般使用兩個字節(jié)來表示一個字符。
碼點
- Unicode 規(guī)定了每個字符的數(shù)字編號,這個編號被稱為
碼點(code point)
。 - 碼點以 U+hex 的形式表示,U+是代表Unicode的前綴,而 hex 是一個16進制數(shù)。取值范圍是從 U+0000 到 U+10FFFF。
- 每個碼點對應(yīng)一個字符,絕大部分的常見字符在最前面的 65536 個字符,范圍是 U+0000到U+FFFF。
- 一般漢字的碼點區(qū)間為 U+2E80 - U+9FFF。
字符平面
- 目前的Unicode分成了17個編組,也稱平面,每個平面有65536個碼點。
- 第一個平面是基本多語言平面,范圍:U+0000 - U+FFFF,多數(shù)常見字符都在該區(qū)間。
- 其他平面則為輔助平面,范圍:U+10000 到 U+10FFFF,如我們在網(wǎng)上常見 Emoji 表情。
碼元
- 碼元(Code Unit)可以理解為對碼點進行編碼時的最小基本單元,碼元是一個整體。而字符編碼的作用就是將Unicode碼點轉(zhuǎn)換成碼元序列。
- Unicode常用的編碼方式有 UTF-8 、UTF-16 和 UTF-32,UTF是Unicode TransferFormat的縮寫。
- UTF-8是8位的單字節(jié)碼元,UTF-16是16位的雙字節(jié)碼元,UTF-32是32位的四字節(jié)碼元。
另外,為什么總看到使用十六進制數(shù)據(jù)來表示如碼點等各種數(shù)據(jù)呢?
因為,兩位的十六進制正好等于一個字節(jié)8位,0xff = 0b11111111。
UTF-8
UTF-8是一種可變長度的字符編碼方式。目前是使用 1 到 4 個字節(jié)來編碼字符。
是互聯(lián)網(wǎng)時代應(yīng)用最廣的一種編碼方式,前端接觸的相對最多。
需要注意的是:漢字一般占3個字節(jié),表情符號一般占4個字節(jié)。
UTF-8的編碼規(guī)則:
- 1個字節(jié)的字符,第一位為0,后7位為碼點,與ASCII相同。
- n個字節(jié)的字符,第一個字節(jié)前面
n
位都是1,n+1位是0,可據(jù)此判斷有幾個字節(jié)。后面的幾個字節(jié)都是10
為開頭2位。
這里規(guī)定的都是前綴,對于字符的碼點,需要進行截取后依次放入除前綴外的其他位,所以UTF-8又被稱為前綴碼。
格式如下表:
通過上表的編碼規(guī)則,我們就可以進行各種轉(zhuǎn)換了。
下面我們以一個中文字符的編碼轉(zhuǎn)換為例,如漢字 '好':
'好'的Unicode碼點:'好'.codePointAt() \\ 22909
,結(jié)果是22909
22909在UTF-8的3字節(jié)數(shù)的編碼區(qū)間 U+0800 (2048) ~ U+FFFF (65535)
22909的二進制值:101100101111101,有15位
而3字節(jié)數(shù)的編碼需要16位,前面補0,根據(jù)表中規(guī)則分成3組:0101 100101 111101
依次填入對應(yīng)的前綴:11100101 10100101 10111101,得到3個字節(jié)
將得到的三個字節(jié)轉(zhuǎn)成十六進制數(shù)據(jù):E5 A5 BD,所以漢字 '好' 的UTF-8就是:E5 A5 BD
我們使用 encodeURI
進行驗證————encodeURI函數(shù)支持將中文進行 UTF-8 編碼:
encodeURI("好") // "%E5%A5%BD"
去除百分號,結(jié)果正好一致。
UTF-16
UTF-16的編碼方式:基本平面的字符占用 2 個字節(jié)(U+0000到U+FFFF),輔助平面的字符占用 4 個字節(jié)(U+010000到U+10FFFF)。
也就是說,UTF-16的編碼長度要么是2個字節(jié)要么是4個字節(jié)。當(dāng)為2字節(jié)時,則實際上是與Unicode相同。
并且還有個原則,在Unicode基本多語言平面內(nèi),從U+D800到U+DFFF之間的碼點區(qū)間是不對應(yīng)字符的。而UTF-16需要利用這塊碼位來對輔助平面的字符進行編碼。
它的具體規(guī)則:
碼點小于U+FFFF,基本字符,不需處理,直接使用,占兩個字節(jié)。
否則,拆分成兩個碼元,四個字節(jié),cp表示碼點:
低位——((cp - 65536) / 1024) + 0xD800,值范圍是 0xD800~0xDBFF;
高位——((cp - 65536) % 1024) + 0xDC00,值范圍是 0xDC00~0xDFFF。
看下面的示例:
1.漢字 '好','好'.codePointAt() // 22909
,碼點小于U+FFFF,直接進行十六進制轉(zhuǎn)換:579D。
2.表情符號 '
