氣死了,之前更詳細(xì)一點(diǎn)的剛寫完,沒保存,不小心弄沒了,搞不回來的那種。于是有了下面的這一概念羅列版。。。
現(xiàn)象
const str = '??'
const str1 = str.split('').reverse().join('')
console.log(str.length) // 2
console.log(str1) // 亂碼
原理探索
術(shù)語和概念
- 字符集:一堆字符組成的集合。
- 字符編碼集:與字符集形成一一映射關(guān)系的指定集合,比如一個(gè)由一堆比特序列組成的集合,與每個(gè)字符對(duì)應(yīng)的那個(gè)比特序列可稱為抽象編碼。
- Unicode:計(jì)算機(jī)科學(xué)領(lǐng)域里對(duì)世界上大部分文字進(jìn)行整理、編碼的一項(xiàng)業(yè)界標(biāo)準(zhǔn)。它定義了一套字符編碼集,里面最多可容納 17 * 2 ^ 16 = 1114112 個(gè)字符。
- 碼位:Unicode 字符編碼集里的抽象編碼。
- 字符編碼集的實(shí)現(xiàn):將抽象編碼進(jìn)一步映射成一個(gè)以某一特定比特長度為單位的序列。
- 碼元:上述特定比特長度代表的那個(gè)東西就是碼元。
- 平面:Unicode 字符分為 17 組編排,每一組稱為一個(gè)平面。
- 基本平面:編碼空間范圍為 U+0000 ~ U+D7FF 和 U+E000 ~ U+FFFF 對(duì)應(yīng)的那個(gè)平面。不連續(xù)的地方分成 U+D800 ~ U+DBFF 和 U+DC00 ~ U+DFFF 兩部分,不對(duì)應(yīng)任何字符。
- 輔助平面:編碼空間范圍為 U+010000 ~ U+10FFFF 的那些平面,共 16 個(gè)。
- UTF-16 是一種 Unicode 字符編碼集的實(shí)現(xiàn)方式,它將基本平面里的字符編碼為 1 個(gè)碼元,其余平面編碼成 2 個(gè)(包含一個(gè)前導(dǎo)代理和一個(gè)后尾代理),一個(gè)碼元為 16 比特,所以它最后的編碼是不定長的。
- 前導(dǎo)代理:對(duì)應(yīng)碼位范圍為
0xD800~0xDBFF(55296 ~ 56319)的碼元,共 2 * 10 = 2048 個(gè)。 - 后尾代理:對(duì)應(yīng)碼位范圍為
0xDC00~0xDFFF(56320 ~ 57343)的碼元,共 2 * 10 = 2048 個(gè)。 - UTF-16 的編碼算法:對(duì)于任意一給定碼位
- 如果這個(gè)碼位屬于基本平面,返回碼位對(duì)應(yīng)的 16 比特的序列。
- 將碼位減去
0x10000得到一個(gè)屬于0x00000~0xFFFFF內(nèi)的 20 比特的序列,將高位的 10 比特(范圍為0x0000~0x03FF)加上0xD800得到前導(dǎo)代理(范圍為0x0000~0x03FF),低位的 10 比特(范圍為0xD800~0xDBFF)加上0xDC00得到后尾代理(范圍為0xDC00~0xDFFF),返回前導(dǎo)代理和后尾代理拼接成的 32 比特的序列,序列中依然是前導(dǎo)代理居高位,后尾代理居低位。
所以依次算法可知,對(duì)于任意一字符串,在 UTF-16 編碼后生成的那串碼元序列中,對(duì)任意一個(gè)碼元進(jìn)行判斷就可以確定這個(gè)碼元所對(duì)應(yīng)的字符的邊界,因?yàn)檫@個(gè)碼元只有如下三種情況:
- 為前導(dǎo)代理
- 為后尾代理
- 對(duì)應(yīng)基本平面里的某個(gè)字符
解釋
??應(yīng)該是一個(gè)在輔助平面的字符,它被 UTF-16 編碼成一個(gè)由兩個(gè)碼元組成的序列,第一個(gè)碼元是前導(dǎo)代理,第二個(gè)是后尾代理:
console.log('??'[0].charCodeAt()) // 55357
console.log('??'[1].charCodeAt()) // 56887
參考: