<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【拓展】談?wù)勛址幋a:Unicode編碼與emoji表情編碼

          共 6830字,需瀏覽 14分鐘

           ·

          2020-08-15 21:12

          開發(fā)過程中,字符編碼是我們一定要掌握的知識(shí)。本文回顧ASCII標(biāo)準(zhǔn),并介紹了Unicode和UTF-8、UTF-16方案間的關(guān)系,各自是如何存儲(chǔ)的,最后介紹了Unicode中emoji表情的構(gòu)成規(guī)則。

          介紹字符編碼前,先要明確概念:

          碼位(碼點(diǎn)),對(duì)應(yīng)編碼術(shù)語中英文中的code point,指的是一個(gè)編碼標(biāo)準(zhǔn)中為某個(gè)字符設(shè)定的數(shù)值,具有唯一性與一一對(duì)應(yīng)性。碼位只規(guī)定了一個(gè)字符對(duì)應(yīng)的數(shù)值,并沒有規(guī)定這個(gè)數(shù)值如何存儲(chǔ),視編碼方案不同有不同的存儲(chǔ)方式。

          像ASCII這樣的簡(jiǎn)單編碼方式,其碼位值就是存儲(chǔ)時(shí)字符實(shí)際上存儲(chǔ)的值,因此不需要特別強(qiáng)調(diào)這個(gè)概念。但后面我們會(huì)看到,Unicode編碼中每個(gè)碼位的值會(huì)對(duì)應(yīng)許多中不同的存儲(chǔ)方案,不同的碼位用幾個(gè)字節(jié)存儲(chǔ)也會(huì)有變化。所以需要理解碼位和字符的一一對(duì)應(yīng)關(guān)系,知道這個(gè)碼位值不受存儲(chǔ)方案的干擾。

          code unit,可以翻譯成編碼單元。搜集資料并沒有找到一個(gè)權(quán)威的官方定義。根據(jù)我的理解,code unit指的是某個(gè)編碼方案存儲(chǔ)方式中最小有意義的單元。例如,UTF-8中最少一個(gè)字節(jié)存儲(chǔ)一個(gè)字符,那么code unit就是8位大小。UTF-16中最少兩個(gè)字節(jié)存儲(chǔ)一個(gè)字符,那么code unit就是16位大小。類似,UTF-32中的code unit是32位大小。從中可以看出,UTF-后面的數(shù)字指的就是該方案下code unit的bit位數(shù)。

          ASCII標(biāo)準(zhǔn)與其擴(kuò)展編碼方案概述

          談到編碼就不得不提到。ASCII碼是我們學(xué)習(xí)計(jì)算機(jī)時(shí)一定會(huì)接觸到的第一個(gè)編碼標(biāo)準(zhǔn),相信大家很熟悉。簡(jiǎn)單總結(jié)一下ASCII碼的特點(diǎn)。

          (1)一個(gè)字符就用一個(gè)字節(jié)表示,而且限制字節(jié)的最高位必須為0,使得總共只能表示2^7=128個(gè)不同字符。

          (2)存在許多沒有字形的字符,如0x00-0x20碼位均屬于此類字符。如格式控制符回車換行、以及計(jì)算機(jī)網(wǎng)絡(luò)中提到的EOF、SOH等等。

          (3)是按照美國(guó)人的習(xí)慣制定的,不能表示除英文字符外的其他眾多字符。? ? ? ? ?這就導(dǎo)致其他國(guó)家用將每個(gè)自己字節(jié)最高位改成1的方式將ASCII編碼擴(kuò)展。擴(kuò)展后多出來的128個(gè)碼位用于添加自己國(guó)家的的語言文字。也有如蘇聯(lián)的國(guó)家將ASCII碼中的`$`美元符號(hào)替換成了其他貨幣符號(hào)。? ? ? ? ?在這種情況下,同一個(gè)碼位在不同編碼標(biāo)準(zhǔn)中有不同的含義,導(dǎo)致各國(guó)的編碼標(biāo)準(zhǔn)無法兼容。

          在大陸國(guó)內(nèi)歷史上用于拓展ASCII的方案則是GB(國(guó)標(biāo))系列編碼方案,該編碼方案歷史悠久,詳細(xì)敘述比較復(fù)雜。由于Windows命令行對(duì)中文默認(rèn)的就是使用GB2312,平時(shí)我們也會(huì)接觸到使用GB系列編碼的字符串,所以我們要大致了解其基本特征。

          (1)兼容ASCII編碼方案。原有的ASCII字符對(duì)應(yīng)的碼位不變,也是使用一個(gè)字節(jié)來存儲(chǔ)。

          (2)除拓展的生僻字外,大部分漢字采用雙字節(jié)編碼。也就是說GB系列編碼存儲(chǔ)也采用的是變長(zhǎng)存儲(chǔ)方案。一旦談到變長(zhǎng)存儲(chǔ),就涉及到字符的定界問題。計(jì)算機(jī)需要知道到底哪幾個(gè)字節(jié)屬于同一個(gè)字符的表示。? ? ? ? ?GB系列方案中,規(guī)定每個(gè)漢字存儲(chǔ)時(shí)的第一個(gè)字節(jié)第一位用1開頭,和ASCII碼做區(qū)分。這樣只要某個(gè)碼位值大于127,就固定表示這是一個(gè)漢字的開始。從GBK編碼之后不要求第二個(gè)字節(jié)的首位以1開頭,不過處理時(shí)只要前面的那個(gè)字節(jié)最高位是1,就能識(shí)別這個(gè)字節(jié)是前面字節(jié)的延續(xù),從而解決定界問題。

          Unicode概述

          如上所述,各國(guó)的編碼之間大部分在ASCII碼范圍可以兼容,但擴(kuò)展后的字符集就不兼容了。因此誕生了Unicode標(biāo)準(zhǔn)以實(shí)現(xiàn)一個(gè)各國(guó)都能統(tǒng)一的字符集。

          不過需要注意的是,Unicode標(biāo)準(zhǔn)的想法僅僅是為每個(gè)字符規(guī)定一個(gè)碼位。不包括具體的存儲(chǔ)方案。不像GB系列方案規(guī)定到每個(gè)字節(jié)在存儲(chǔ)時(shí)最高位必須為1這么詳細(xì),這個(gè)標(biāo)準(zhǔn)僅僅給字符分配了碼位而已。

          在閱讀介紹Unicode的其他資料時(shí),需要理解Unicode方案為每個(gè)字符制定的碼位的表示方式及規(guī)則。

          一般Unicode的碼位表示成U+XXXXXX 的形式,X 代表一個(gè)十六制數(shù)字,表示形式的范圍在 4-6 位之間,也就是U+0000 ~ U+10FFFF間。當(dāng)碼位值不足 4 位時(shí)前面補(bǔ) 0 補(bǔ)足 4 位,超過則按是幾位就是幾位。

          至于為什么上限是10FFFF,和目前的碼位劃分方式有關(guān)。為了方便碼位的管理,便于碼位的分配,Unicode將編碼空間均分成 17 個(gè) 65536 大小的分區(qū),每個(gè)分區(qū)稱為**平面(plane)。**共存在65536*17=1114112個(gè)碼位,換算成16進(jìn)制從0開始算起就是U+10FFFF了。

          其中,第零平面,也就是U+0000~U+FFFF的碼位叫做基本多語言平面,簡(jiǎn)稱BMP(Basic Multilingual Plane)。我們平常的使用的大部分語言文字,包括除生僻字以外的漢字碼位都規(guī)定在這個(gè)平面中。其余平面叫做補(bǔ)充平面(Supplementary Planes)。

          注意,BMP里存在一種特殊區(qū)域: 代理區(qū)(Surrogate)。Unicode標(biāo)準(zhǔn)規(guī)定U+D800 - U+DFFF的值不對(duì)應(yīng)于任何字符。后面可以看到,UTF-16就巧妙地利用了這一段空白區(qū)域進(jìn)行了編碼的轉(zhuǎn)換。

          如前面所述,由于Unicode 只是給每個(gè)字符規(guī)定了一個(gè)碼位,是連續(xù)分配的,而沒有考慮到一些其他的沖突問題,即前面說過的字符定界的問題。碼位值越大,需要完整表示使用的二進(jìn)制位數(shù)越多,假如直接把碼位值轉(zhuǎn)換成二進(jìn)制存儲(chǔ),在 Unicode 中往后的字符可能就需要 3 個(gè)字節(jié)甚至4個(gè)字節(jié)來表示了。這就導(dǎo)致定界問題,如何確定到底用幾個(gè)字節(jié)來表示一個(gè)字符呢?這是Unicode標(biāo)準(zhǔn)沒有指明的。涉及到具體存儲(chǔ)光看Unicode編碼無法解決問題,如何存儲(chǔ)還需要另外的方案。

          容易想到,最簡(jiǎn)單的做法是讓所有的字符類型都用四個(gè)字節(jié)定長(zhǎng)存儲(chǔ),計(jì)算機(jī)在讀取時(shí)統(tǒng)一讀取四個(gè)字節(jié)作為一個(gè)碼位來理解,這樣就解決了定界問題。UTF-32采用的是這種方案,這樣解決了編碼問題,但是卻造成了空間的極大浪費(fèi)。大部分常用字符位于BMP內(nèi),本來兩個(gè)字節(jié)就能存儲(chǔ),卻統(tǒng)一變成了四個(gè)字節(jié)來存儲(chǔ),使得存儲(chǔ)空間浪費(fèi)了很多。

          因此,目前采用廣泛的Unicode編碼存儲(chǔ)方案都是變長(zhǎng)的存儲(chǔ)方案,如UTF-8和UTF-16。

          UTF-8


          UTF-8的實(shí)現(xiàn)方案其實(shí)非常簡(jiǎn)單,上面這張表就可以看懂。特性總結(jié)為:

          (1)與ASCII碼兼容。也就是對(duì)于ASCII字符按照原有的字符,第一位設(shè)為 0,后面的 7 位對(duì)應(yīng)這個(gè)字符的 Unicode 碼位。這樣用 ASCII 碼編碼的文檔用 UTF-8 編碼打開不會(huì)出現(xiàn)問題。

          (2)除了ASCII以外,其他碼位需要用多個(gè)字節(jié)表示。對(duì)于這種類型的字符,第一個(gè)字節(jié)的前面使用110~1110幾個(gè)不同的前綴來標(biāo)識(shí),而后面的字節(jié)則以10開頭,表示這個(gè)字節(jié)是前面的延續(xù)。這種方式使計(jì)算機(jī)看到了第一個(gè)字節(jié)就能知道后面是還有幾個(gè)字節(jié)屬于同一個(gè)字符,也能在看到以10開頭的字節(jié)就知道這個(gè)字節(jié)不是一個(gè)字符的開始。

          根據(jù)上表實(shí)現(xiàn)unicode到UTF-8的轉(zhuǎn)換也比較簡(jiǎn)單,知道unicode編碼后查表找到其對(duì)應(yīng)UTF-8編碼的范圍,從這個(gè)范圍開頭往后尋找其位置即可。有興趣的同學(xué)可以自己嘗試。

          使用UTF-8編碼時(shí),大部分漢字轉(zhuǎn)換后需要用三字節(jié)存儲(chǔ)。

          UTF-16

          UTF-16實(shí)現(xiàn)方案則介于UTF-8和UTF-32之間。對(duì)最常用的基本平面中字符的存儲(chǔ)空間進(jìn)行了壓縮,使得漢字只需要兩個(gè)字節(jié)就可以存儲(chǔ)。

          由于基本平面的碼位值從U+0000-U+FFFF,剛好用 2 個(gè)字節(jié)就可以存放,所以UTF-16規(guī)定基本平面中的字符占用2個(gè)字節(jié),輔助平面的字符占用 4 個(gè)字節(jié)。UTF-16 的編碼長(zhǎng)度要么是 2 個(gè)字節(jié),要么是 4 個(gè)字節(jié)。

          那么UTF-16又是怎么解決字符存儲(chǔ)的時(shí)不同字符的邊界問題的呢?

          前面提到,在基本平面內(nèi),從 U+D800 到 U+DFFF 的碼位是代理區(qū),不對(duì)應(yīng)任何字符。因此,UTF-16就用了這一段區(qū)域巧妙地解決了邊界問題。

          UTF-16將代理區(qū)進(jìn)一步劃分成兩部分。0xD800~0xDBFF,稱為高半代理(leading surrogate)。0xDC00~0xDFFF分區(qū), 稱為低半代理(trailing surrogate)。

          不難得到,高半代理范圍含有0xDBFF-0xD800+1=0x0400=2^10個(gè)碼位,低半代理范圍也有0x0400=2^10個(gè)碼位。

          除去基本平面已經(jīng)分配存儲(chǔ)方案的碼位后,輔助平面還剩下0x10FFFF-0x010000+1 = 0x0F0000,約為2^20個(gè)碼位。這些碼位還需要分配存儲(chǔ)方案。假如將這些碼位值全部存儲(chǔ)成二進(jìn)制數(shù),需要至少20位來存儲(chǔ),至少也需要四個(gè)字節(jié)。

          UTF-16的做法是,將這四個(gè)字節(jié)的前兩個(gè)字節(jié)映射到高半代理表示的范圍,后兩個(gè)字節(jié)映射到低半代理的范圍。高半代理和低半代理總共能表示2^10*2^10=2^20個(gè)數(shù),剛好能為每個(gè)輔助平面還剩下的2^20個(gè)碼位分配存儲(chǔ)方案。

          這樣,按兩個(gè)字節(jié)兩個(gè)字節(jié)讀取的方式判別,假如讀到的值不在代理區(qū)內(nèi),就證明這就是一個(gè)BMP內(nèi)的字符。假如讀到的值位于前導(dǎo)代理范圍內(nèi),證明這是一個(gè)四字節(jié)輔助字符的開頭,后面兩個(gè)字節(jié)是這個(gè)字符的延續(xù)。假如讀到的值位于后導(dǎo)代理范圍內(nèi),證明前面兩個(gè)字節(jié)也屬于這個(gè)輔助字符的一部分。

          這樣就解決了字符定界問題。

          有了具體的存儲(chǔ)方案,unicode在制定標(biāo)準(zhǔn)時(shí)就只需要為字符分配抽象的碼位了,具體的存儲(chǔ)由采用UTF-8還是UTF-16方案來定,極大方便了編碼的制定。

          因此,下面討論emoji表情編碼時(shí)不需要討論其存儲(chǔ)方案,只需要討論其邏輯層次上的Unicode編碼。

          emoji表情的unicode編碼

          emoji表情大家應(yīng)該也比較熟悉了。像常用的??用語就是emoji表情組成的。我們?cè)賮碚務(wù)勗趗nicode對(duì)于emoji表情的編碼。這里也要注意,Unicode只是規(guī)定了 emoji 的碼位和含義,以及用文字指導(dǎo)它們代表的表情長(zhǎng)什么樣,并沒有規(guī)定它的具體樣式。渲染的工作則由各個(gè)系統(tǒng)自己實(shí)現(xiàn)。如果用戶的系統(tǒng)沒有實(shí)現(xiàn)某個(gè)emoji表情的渲染,就會(huì)顯示成一個(gè)空方框。

          在MAC中,輸入ctrl + cmd + 空格后在彈出的面板里添加unicode代碼表就可以看見每個(gè)unicode碼位對(duì)應(yīng)的字符了。其中可以非常方便地查詢到字符對(duì)應(yīng)到編碼值。如下圖所示。

          從U+1F300開始,存放的這些小表情就是emoji表情。

          可以發(fā)現(xiàn)簡(jiǎn)單的笑臉對(duì)應(yīng)的是Unicode的一個(gè)碼位,但一個(gè)蒸餾器對(duì)應(yīng)了兩個(gè)碼位,一個(gè)金發(fā)男子表情竟然對(duì)應(yīng)了五個(gè)碼位。我們看到,一個(gè)emoji表情也是變長(zhǎng)存儲(chǔ)的,而且一個(gè)表情可占用多個(gè)碼位組成。那么這些表情的渲染又有什么規(guī)則呢?

          ?

          要解答這個(gè)問題,需要閱讀unicode官方介紹emoji表情的文檔。

          https://www.unicode.org/reports/tr51/#Emoji_Properties_and_Data_Files

          關(guān)于emoji表情涉及到許多定義,見其中1.4節(jié)。其中有這樣的定義

          text_presentation_selector := \x{FE0E}

          U+FE0E的含義是,一個(gè)text_presentation_selector,大概意思是文本表示選擇符。

          text_presentation_sequence := emoji_character text_presentation_selector

          這個(gè)定義表明,加上了這個(gè)U+FE0E修飾的emoji字符會(huì)構(gòu)成一個(gè)文本表示序列。所以,這個(gè)text_presentation_selectorU+FE0E是用來修飾前面的emoji表情,指定其展示方式的。

          不過筆者使用的macbook對(duì)這種文本表示emoji的表情還沒有支持,使用上面的輸入法添加U+FE0E選擇符后并沒有成功渲染出emoji的文本表示形態(tài)。

          類似的,還存在定義

          emoji_presentation_selector := \x{FE0F}emoji_presentation_sequence := emoji_character emoji_presentation_selector

          加上了U+FE0F修飾的emoji字符會(huì)構(gòu)成一個(gè)emoji表示序列。

          例如這個(gè)蒸餾器表情就是由U+FE0F修飾U+2697產(chǎn)生的,可以利用剛才提到的mac自帶輸入法嘗試一下。

          輸入查找Unicode編碼U+2697輸出得到?這個(gè)小蒸餾器符號(hào)

          假如在輸入法中搜索U+FE0F找到

          再連在前面輸入的U+2697?符號(hào)后面后面輸入這個(gè)修飾符就得到了??這個(gè)emoji表情。

          由此,我們理解了U+FE0F的作用,相當(dāng)于一個(gè)修飾符,在渲染文字時(shí)如果遇到了U+FE0F就和前面的字符組合一下,改成渲染成一個(gè)emoji表情的形式。這樣就可以理解上面的蒸餾器為什么占用了二個(gè)Unicode碼位了。本身這個(gè)表情就是由其他字符加上修飾符組合而成的。

          此外, 還有另一些比較有趣的定義。

          emoji_modifier := \p{Emoji_Modifier}emoji_modifier_base := \p{Emoji_Modifier_Base}emoji_modifier_sequence := emoji_modifier_base emoji_modifier

          意思是,一個(gè)作為base的表情加上一個(gè)modifier修飾符后會(huì)構(gòu)成一個(gè)emoji修飾符序列。有些表情會(huì)具有base屬性,有些則具有modifer屬性。下面來看個(gè)例子。

          在輸入法中找到U+1F471號(hào)字符,單獨(dú)輸出。?

          ??

          再找到U+1F3FF號(hào)字符,單獨(dú)輸出

          這次再將兩個(gè)表情連在一起輸出,結(jié)果變成了一個(gè)

          類似還有其他的組合規(guī)則,如國(guó)旗,鍵盤等都有各自的組合規(guī)則,也是用具有不同屬性的unicode字符組合而成。詳情可翻閱文檔查閱。

          另外,介紹Emoji表情編碼還不得不提到一個(gè)特殊字符ZWJ,全程zero-width joiner,意思是零寬度連字符,占用碼位U+200D。這個(gè)字符不是為emoji單獨(dú)服務(wù)的。用于插入在某些語言的字符中,使左右的兩個(gè)字符連在一起產(chǎn)生連字效果,合成單個(gè)人類可讀的字符。

          具體到emoji表情中,某些單個(gè)顯示表情也會(huì)由多個(gè)獨(dú)立的ZWJ連字合成得到。

          舉例

          可以看到,這個(gè)紅發(fā)女子就是由U+1F469和U+1F9B0使用U+200D連字得到的。下面我們來輸入看看這兩個(gè)字符分別代表什么。

          U+1F469?

          ?

          U+1F9B0

          這表明,U+1F469代表的是一個(gè)女子,而U+1F9B0單個(gè)表情代表的是頭發(fā)的顏色,利用U+200D組合到一起,就得到了紅發(fā)女子這個(gè)表情。

          有了這些組合方式的基礎(chǔ),再回頭看前面的金發(fā)黑人男子就不難理解為什么它由那么多Unicode字符組成了。本質(zhì)也是通過前面所述的三種表示規(guī)則組成得到的。大家可以用輸入法每個(gè)字符依次自行輸入一遍金發(fā)黑人男子表情,就能理解其合成的方式。

          總結(jié)與展望

          限于時(shí)間與篇幅,本文僅僅回顧ASCII標(biāo)準(zhǔn),并介紹了Unicode和UTF-8、UTF-16方案間的關(guān)系,各自是如何存儲(chǔ)的,最后介紹了Unicode中emoji表情的構(gòu)成規(guī)則。

          掌握這些編碼的通用基礎(chǔ)知識(shí),是后續(xù)學(xué)習(xí)各語言各平臺(tái)對(duì)字符串處理規(guī)則的基礎(chǔ)。每個(gè)語言存儲(chǔ)字符串時(shí)采用UTF-8還是UTF-16都會(huì)有所不同,視每個(gè)語言、每個(gè)編譯器的具體實(shí)現(xiàn)而定。

          特別是對(duì)于emoji表情,由于其組成規(guī)則復(fù)雜,可能是由多個(gè)Unicode字符組合而成的。所以在處理含有emoij表情的字符串時(shí),使用索引、統(tǒng)計(jì)長(zhǎng)度和其他字符串比較運(yùn)算都需要一些算法支持。iOS開發(fā)使用Swift語言時(shí),emoji表情能作為單個(gè)的字符進(jìn)行索引,特性強(qiáng)大,但同時(shí)也因此造成了Swift語言的字符串使用起來并不方便。

          后續(xù)的文章會(huì)逐步介紹Swift語言對(duì)字符串的實(shí)現(xiàn)機(jī)制,研讀swift是如何處理含義emoji表情的字符串。



          1. JavaScript 重溫系列(22篇全)
          2. ECMAScript 重溫系列(10篇全)
          3. JavaScript設(shè)計(jì)模式 重溫系列(9篇全)
          4.?正則 / 框架 / 算法等 重溫系列(16篇全)
          5.?Webpack4 入門(上)||?Webpack4 入門(下)
          6.?MobX 入門(上)?||??MobX 入門(下)
          7.?70+篇原創(chuàng)系列匯總

          回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

          點(diǎn)這,與大家一起分享本文吧~
          瀏覽 95
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美黄色做爱视频 | 亚洲三级先锋影音 | 大香蕉伊人导航 | 99操天天干 | 日韩一级性爱 |