Unicode和UTF-8履因、UTF-16障簿、UTF-32

寫在前面

如果你是iOS開發(fā)者,并且在處理NSString字符上遇到了一些問(wèn)題栅迄,強(qiáng)烈建議去看看Objc中國(guó)上關(guān)于 NSString 與 Unicode站故。

簡(jiǎn)介

Unicode對(duì)世界上大部分的文字系統(tǒng)進(jìn)行了整理、編碼毅舆,使得電腦可以用更為簡(jiǎn)單的方式來(lái)呈現(xiàn)和處理文字西篓。這是維基百科對(duì)Unicode下的定義。
Unicode的實(shí)現(xiàn)方式包含了UTF-8憋活、UTF-16(字符用兩個(gè)字節(jié)或者四個(gè)字節(jié)表示)和UTF-32(用四個(gè)字節(jié)來(lái)表示)岂津,下面對(duì)面一一進(jìn)行介紹。

UTF-8

UTF-8的最明顯的一個(gè)特點(diǎn)是它是變長(zhǎng)的悦即,它可以使用1到4個(gè)字節(jié)表示一個(gè)符號(hào)吮成,根據(jù)不同的符號(hào)變化字節(jié)長(zhǎng)度
先把阮一峰在《字符編碼筆記:ASCII辜梳,Unicode和UTF-8》中對(duì)UTF-8的編寫規(guī)則的一個(gè)總結(jié)放出來(lái)粱甫。

??????UTF-8的編碼規(guī)則很簡(jiǎn)單,只有二條:
1作瞄、對(duì)于單字節(jié)的符號(hào)茶宵,字節(jié)的第一位設(shè)為0,后面7位為這個(gè)符號(hào)的unicode碼粉洼。因此對(duì)于英語(yǔ)字母节预,UTF-8編碼和ASCII碼是相同的叶摄。
2属韧、對(duì)于n字節(jié)的符號(hào)(n>1),第一個(gè)字節(jié)的前n位都設(shè)為1蛤吓,第n+1位設(shè)為0宵喂,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進(jìn)制位会傲,全部為這個(gè)符號(hào)的unicode碼锅棕。

提出問(wèn)題:第一個(gè)字節(jié)前面n位設(shè)1是為了知道當(dāng)前字符占用多少字節(jié),而后面字節(jié)的前兩位字節(jié)為什么要設(shè)置為10呢淌山?下面會(huì)馬上進(jìn)行解釋裸燎。
現(xiàn)在我們來(lái)具體的分析一下Unicode的不同范圍:下面中前兩個(gè)描述是否為ASCII,后兩個(gè)描述多字節(jié)序列

  • U+0000到U+007F(ASCII)
    從U+0000到U+007F被編碼為0x00~0x7F的單字節(jié)泼疑,這是ASCII碼的所有字符德绿,一共128個(gè)字符,所以Unicode是完全用來(lái)容納ASCII的。

    回答上面提出的問(wèn)題:后面字節(jié)的前兩位一律設(shè)為10(10000000也就是80)是因?yàn)楸仨氁笥?F才和ASCII碼分開移稳。

  • 大于 U+007F(非 ASCII)
    所有大于 U+007F 的字符被編碼為一串多字節(jié)序列蕴纳,這樣就可以區(qū)分一串多字節(jié)序列是多字節(jié)碼還是 ASCII 碼。

  • 0xFE 和 0xFF 不會(huì)被用于 UTF-8 編碼中个粱。

  • 多字節(jié)序列的第一個(gè)字節(jié)在0xC00xFD中古毛,剩余字節(jié)在0x800xBF內(nèi)。
    這里解釋一下為什么第一個(gè)是在0xC0~0xFD中都许,理解這里需要再回去看看上面注意中提到的Unicode編碼規(guī)則稻薇。因?yàn)楸硎镜氖嵌嘧止?jié)就表明n是大于1的,所以第一個(gè)字節(jié)最小的值為:11000000即C0(表明當(dāng)前有兩個(gè)字節(jié)胶征。每四位表示一個(gè)十六進(jìn)制數(shù)颖低,這也是為什么在編程的時(shí)候喜歡用十六進(jìn)制數(shù)的原因),如果在沒有限制的情況下弧烤,通過(guò)上面的結(jié)論我們可以得到第一個(gè)字節(jié)能表示的最大的數(shù)是0xFE(11111110)忱屑,就是前面7位設(shè)置1最后一位設(shè)置為0,但是上面一條中提到不包含F(xiàn)E暇昂,所以第一個(gè)字節(jié)的最大值為0xFD(11111101)莺戒。
    同理因?yàn)?code>后面字節(jié)的前兩位一律設(shè)為10所以多字節(jié)除了第一個(gè)字節(jié)的其他字節(jié)最大值為10111111(BF)

總結(jié)
UTF-8 編碼字符最長(zhǎng)可達(dá)六個(gè)字節(jié)

Unicode 字符:             UTF-8 碼:
U-00000000 - U-0000007F:    0xxxxxxx      ///表示ASCII
U-00000080 - U-000007FF:    110xxxxx 10xxxxxx      ///
U-00000800 - U-0000FFFF:    1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF:    11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF:    111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF:    1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

舉個(gè)例子:漢字“王”的Unicode碼為U+73B8轉(zhuǎn)換為二進(jìn)制為:0111 0011 1010 1000急波,"73b8"位于上面的第三類从铲,把轉(zhuǎn)換的16個(gè)二進(jìn)制依次放入(一定是一次放入不管空格的)上訴的x中:

11100111 10001110 10101000
/// 同樣我們來(lái)驗(yàn)證一下阮一峰舉例的“嚴(yán)”字
/// 4E25 同樣屬于上訴的第三類 ,對(duì)應(yīng)的二進(jìn)制 => 0100 1110 0010 0101
/// 11100100 10111000 10100101 得到的結(jié)果和他文章中的一樣澄暮。

到這里我自己覺得應(yīng)該是把UTF-8的編碼方式說(shuō)清楚了名段,最后再來(lái)一個(gè)編碼的順序(很適合于我的方式)

編碼的順序
對(duì)于單字節(jié):
直接將其轉(zhuǎn)換為八位的二進(jìn)制就可以了;
對(duì)于多字節(jié):

  • 1.找到Unicode碼對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)
  • 2.查看該Unicode碼在分類中屬于第幾類
  • 3.一次填入二進(jìn)制碼

UTF-16

UTF-16Unicode字符編碼五層次模型的第三層:字符編碼表(Character Encoding Form泣懊,也稱為"storage format")的一種實(shí)現(xiàn)方式伸辟。即把Unicode字符集的抽象碼位映射為16位長(zhǎng)的整數(shù)(即碼元)的序列,用于數(shù)據(jù)存儲(chǔ)或傳遞馍刮。
這里需要說(shuō)明一下基本多文種平面-BMP輔助平面-SMP信夫,在維基百科中每一個(gè)平面相關(guān)的圖片下面都說(shuō)了"每個(gè)寫著數(shù)字的格子代表256個(gè)碼點(diǎn)",即00~FF卡啰。例如:位于BMP中00格子中的一個(gè)碼點(diǎn)表示為:0x00E5静稻。
下面同樣用一下阮一峰的規(guī)則總結(jié):

基本平面的字符占用2個(gè)字節(jié),輔助平面的字符占用4個(gè)字節(jié)匈辱。也就是說(shuō)振湾,UTF-16的編碼長(zhǎng)度要么是2個(gè)字節(jié)(U+0000到U+FFFF),要么是4個(gè)字節(jié)(U+010000到U+10FFFF)亡脸。

為了能夠區(qū)分它本身是一個(gè)字符押搪,還是需要跟其他兩個(gè)字節(jié)放在一起解讀佛南。在BMP中,從U+D800U+DFFF之間BMP的區(qū)段是永久保留不映射到字符(從維基百科的圖中D8~DF之間表示unallocated code points)嵌言。

UTF-16結(jié)論(D800~DFFF)

具體來(lái)說(shuō)嗅回,輔助平面的字符位共有pow(2,20)個(gè),也就是說(shuō)摧茴,對(duì)應(yīng)這些字符至少需要20個(gè)二進(jìn)制位绵载。UTF-16將這20位拆成兩半,前10位映射在U+D800到U+DBFF(空間大小pow(2,10))苛白,稱為高位(H)娃豹,后10位映射在U+DC00到U+DFFF(空間大小pow(2,10)),稱為低位(L)购裙。這意味著懂版,一個(gè)輔助平面的字符,被拆成兩個(gè)基本平面的字符表示躏率。

HHHH HHHH HHLL LLLL LLLL

????????注意-結(jié)論
高位:D800~DBFF;
低位:DC00~DFFF;

所以躯畴,當(dāng)我們遇到兩個(gè)字節(jié),發(fā)現(xiàn)它的碼點(diǎn)在U+D800到U+DBFF之間薇芝,就可以斷定蓬抄,緊跟在后面的兩個(gè)字節(jié)的碼點(diǎn),應(yīng)該在U+DC00到U+DFFF之間夯到,這四個(gè)字節(jié)必須放在一起解讀嚷缭。(而不會(huì)拆成兩個(gè)字節(jié)來(lái)讀)

解釋一下這里為什么是pow(2,20),在基本平面之外有16個(gè)輔助平面(即pow(2,4))耍贾,而每一個(gè)輔助平面pow(2,16)個(gè)碼位(輔助平面和基本平面一樣阅爽,每個(gè)碼位里面都包含了256個(gè)碼點(diǎn))。

///輔助平面字符荐开,轉(zhuǎn)碼公式付翁。
/// js代碼
H = Math.floor((c-0x10000) / 0x400)+0xD800
L = (c - 0x10000) % 0x400 + 0xDC00

H為上文提到的高位,L位上文提到的低位誓焦。
舉例說(shuō)明一下:

/// 對(duì)于小于0xFFFF的即基本平面的字符胆敞,為兩個(gè)字節(jié)
U+8D9E = 0x8D9E  ///對(duì)應(yīng)的二進(jìn)制格式為:10001101 10011110

/// 對(duì)出于輔助平面的字符
/// 對(duì)于U+1D306
H = Math.floor((0x1D306-0x10000) / 0x400)+0xD800 = d834
L = (0x1D306 - 0x10000) % 0x400 + 0xDC00 = df06

UTF-32

因?yàn)閁TF-32對(duì)每個(gè)字符都使用4字節(jié),就空間而言杂伟,是非常沒有效率的。特別地仍翰,非基本多文種平面的字符在大部分文件中通常很罕見赫粥,以致于它們通常被認(rèn)為不存在占用空間大小的討論,使得UTF-32通常會(huì)是其它編碼的二到四倍予借。

結(jié)論

所以當(dāng)我們?cè)谑褂米址臅r(shí)候越平,通常使用length的時(shí)候频蛔,要看他的編碼方式,并不是一個(gè)字符就代表了一個(gè)字節(jié)有可能是兩個(gè)字節(jié)秦叛、四個(gè)字節(jié)甚至可能最多能到6個(gè)字節(jié)都是有可能的晦溪,這應(yīng)該就能理解Swift中對(duì)于字符串的處理了。

參考文獻(xiàn)

Objc中國(guó)上關(guān)于 NSString 與 Unicode
Unicode代碼圖表
字符編碼筆記:ASCII挣跋,Unicode和UTF-8
什么是UTF-8
Unicode與JavaScript詳解
Unicode編碼及其實(shí)現(xiàn):UTF-16三圆、UTF-8,and more
UTF-16
UTF-32

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末避咆,一起剝皮案震驚了整個(gè)濱河市舟肉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌查库,老刑警劉巖路媚,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異樊销,居然都是意外死亡整慎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門围苫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)院领,“玉大人,你說(shuō)我怎么就攤上這事够吩”热唬” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵周循,是天一觀的道長(zhǎng)强法。 經(jīng)常有香客問(wèn)我,道長(zhǎng)湾笛,這世上最難降的妖魔是什么饮怯? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮嚎研,結(jié)果婚禮上蓖墅,老公的妹妹穿的比我還像新娘。我一直安慰自己临扮,他們只是感情好论矾,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杆勇,像睡著了一般贪壳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚜退,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天闰靴,我揣著相機(jī)與錄音彪笼,去河邊找鬼。 笑死蚂且,一個(gè)胖子當(dāng)著我的面吹牛配猫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杏死,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼泵肄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了识埋?” 一聲冷哼從身側(cè)響起凡伊,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窒舟,沒想到半個(gè)月后系忙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惠豺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年银还,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洁墙。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛹疯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出热监,到底是詐尸還是另有隱情捺弦,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布孝扛,位于F島的核電站列吼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏苦始。R本人自食惡果不足惜寞钥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陌选。 院中可真熱鬧理郑,春花似錦、人聲如沸咨油。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)臼勉。三九已至邻吭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宴霸,已是汗流浹背囱晴。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓢谢,地道東北人畸写。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像氓扛,于是被迫代替她去往敵國(guó)和親枯芬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容