【拓展】談談字符編碼:Unicode編碼與emoji表情編碼

開發過程當中,字符編碼是咱們必定要掌握的知識。本文回顧ASCII標準,並介紹了Unicode和UTF-八、UTF-16方案間的關係,各自是如何存儲的,最後介紹了Unicode中emoji表情的構成規則。前端

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

碼位(碼點),對應編碼術語中英文中的code point,指的是一個編碼標準中爲某個字符設定的數值,具備惟一性與一一對應性。碼位只規定了一個字符對應的數值,並無規定這個數值如何存儲,視編碼方案不一樣有不一樣的存儲方式。算法

像ASCII這樣的簡單編碼方式,其碼位值就是存儲時字符實際上存儲的值,所以不須要特別強調這個概念。但後面咱們會看到,Unicode編碼中每一個碼位的值會對應許多中不一樣的存儲方案,不一樣的碼位用幾個字節存儲也會有變化。因此須要理解碼位和字符的一一對應關係,知道這個碼位值不受存儲方案的干擾。swift

code unit,能夠翻譯成編碼單元。蒐集資料並無找到一個權威的官方定義。根據個人理解,code unit指的是某個編碼方案存儲方式中最小有意義的單元。例如,UTF-8中最少一個字節存儲一個字符,那麼code unit就是8位大小。UTF-16中最少兩個字節存儲一個字符,那麼code unit就是16位大小。相似,UTF-32中的code unit是32位大小。從中能夠看出,UTF-後面的數字指的就是該方案下code unit的bit位數。設計模式

ASCII標準與其擴展編碼方案概述

談到編碼就不得不提到。ASCII碼是咱們學習計算機時必定會接觸到的第一個編碼標準,相信你們很熟悉。簡單總結一下ASCII碼的特色。微信

(1)一個字符就用一個字節表示,並且限制字節的最高位必須爲0,使得總共只能表示2^7=128個不一樣字符。網絡

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

(3)是按照美國人的習慣制定的,不能表示除英文字符外的其餘衆多字符。         這就致使其餘國家用將每一個本身字節最高位改爲1的方式將ASCII編碼擴展。擴展後多出來的128個碼位用於添加本身國家的的語言文字。也有如蘇聯的國家將ASCII碼中的`$`美圓符號替換成了其餘貨幣符號。         在這種狀況下,同一個碼位在不一樣編碼標準中有不一樣的含義,致使各國的編碼標準沒法兼容。框架

在大陸國內歷史上用於拓展ASCII的方案則是GB(國標)系列編碼方案,該編碼方案歷史悠久,詳細敘述比較複雜。因爲Windows命令行對中文默認的就是使用GB2312,平時咱們也會接觸到使用GB系列編碼的字符串,因此咱們要大體瞭解其基本特徵。編輯器

(1)兼容ASCII編碼方案。原有的ASCII字符對應的碼位不變,也是使用一個字節來存儲。

(2)除拓展的生僻字外,大部分漢字採用雙字節編碼。也就是說GB系列編碼存儲也採用的是變長存儲方案。一旦談到變長存儲,就涉及到字符的定界問題。計算機須要知道到底哪幾個字節屬於同一個字符的表示。         GB系列方案中,規定每一個漢字存儲時的第一個字節第一位用1開頭,和ASCII碼作區分。這樣只要某個碼位值大於127,就固定表示這是一個漢字的開始。從GBK編碼以後不要求第二個字節的首位以1開頭,不過處理時只要前面的那個字節最高位是1,就能識別這個字節是前面字節的延續,從而解決定界問題。

Unicode概述

如上所述,各國的編碼之間大部分在ASCII碼範圍能夠兼容,但擴展後的字符集就不兼容了。所以誕生了Unicode標準以實現一個各國都能統一的字符集。

不過須要注意的是,Unicode標準的想法僅僅是爲每一個字符規定一個碼位。不包括具體的存儲方案。不像GB系列方案規定到每一個字節在存儲時最高位必須爲1這麼詳細,這個標準僅僅給字符分配了碼位而已。

在閱讀介紹Unicode的其餘資料時,須要理解Unicode方案爲每一個字符制定的碼位的表示方式及規則。

通常Unicode的碼位表示成U+XXXXXX 的形式,X 表明一個十六制數字,表示形式的範圍在 4-6 位之間,也就是U+0000 ~ U+10FFFF間。當碼位值不足 4 位時前面補 0 補足 4 位,超過則按是幾位就是幾位。

至於爲何上限是10FFFF,和目前的碼位劃分方式有關。爲了方便碼位的管理,便於碼位的分配,Unicode將編碼空間均分紅 17 個 65536 大小的分區,每一個分區稱爲**平面(plane)。**共存在65536*17=1114112個碼位,換算成16進制從0開始算起就是U+10FFFF了。

其中,第零平面,也就是U+0000~U+FFFF的碼位叫作基本多語言平面,簡稱BMP(Basic Multilingual Plane)。咱們日常的使用的大部分語言文字,包括除生僻字之外的漢字碼位都規定在這個平面中。其他平面叫作補充平面(Supplementary Planes)

注意,BMP裏存在一種特殊區域: 代理區(Surrogate)。Unicode標準規定U+D800 - U+DFFF的值不對應於任何字符。後面能夠看到,UTF-16就巧妙地利用了這一段空白區域進行了編碼的轉換。

如前面所述,因爲Unicode 只是給每一個字符規定了一個碼位,是連續分配的,而沒有考慮到一些其餘的衝突問題,即前面說過的字符定界的問題。碼位值越大,須要完整表示使用的二進制位數越多,假如直接把碼位值轉換成二進制存儲,在 Unicode 中日後的字符可能就須要 3 個字節甚至4個字節來表示了。這就致使定界問題,如何肯定到底用幾個字節來表示一個字符呢?這是Unicode標準沒有指明的。涉及到具體存儲光看Unicode編碼沒法解決問題,如何存儲還須要另外的方案。

容易想到,最簡單的作法是讓全部的字符類型都用四個字節定長存儲,計算機在讀取時統一讀取四個字節做爲一個碼位來理解,這樣就解決了定界問題。UTF-32採用的是這種方案,這樣解決了編碼問題,可是卻形成了空間的極大浪費。大部分經常使用字符位於BMP內,原本兩個字節就能存儲,卻統一變成了四個字節來存儲,使得存儲空間浪費了不少。

所以,目前採用普遍的Unicode編碼存儲方案都是變長的存儲方案,如UTF-8和UTF-16。

UTF-8


UTF-8的實現方案其實很是簡單,上面這張表就能夠看懂。特性總結爲:

(1)與ASCII碼兼容。也就是對於ASCII字符按照原有的字符,第一位設爲 0,後面的 7 位對應這個字符的 Unicode 碼位。這樣用 ASCII 碼編碼的文檔用 UTF-8 編碼打開不會出現問題。

(2)除了ASCII之外,其餘碼位須要用多個字節表示。對於這種類型的字符,第一個字節的前面使用110~1110幾個不一樣的前綴來標識,然後面的字節則以10開頭,表示這個字節是前面的延續。這種方式使計算機看到了第一個字節就能知道後面是還有幾個字節屬於同一個字符,也能在看到以10開頭的字節就知道這個字節不是一個字符的開始。

根據上表實現unicode到UTF-8的轉換也比較簡單,知道unicode編碼後查表找到其對應UTF-8編碼的範圍,從這個範圍開頭日後尋找其位置便可。有興趣的同窗能夠本身嘗試。

使用UTF-8編碼時,大部分漢字轉換後須要用三字節存儲。

UTF-16

UTF-16實現方案則介於UTF-8和UTF-32之間。對最經常使用的基本平面中字符的存儲空間進行了壓縮,使得漢字只須要兩個字節就能夠存儲。

因爲基本平面的碼位值從U+0000-U+FFFF,恰好用 2 個字節就能夠存放,因此UTF-16規定基本平面中的字符佔用2個字節,輔助平面的字符佔用 4 個字節。UTF-16 的編碼長度要麼是 2 個字節,要麼是 4 個字節。

那麼UTF-16又是怎麼解決字符存儲的時不一樣字符的邊界問題的呢?

前面提到,在基本平面內,從 U+D800 到 U+DFFF 的碼位是代理區,不對應任何字符。所以,UTF-16就用了這一段區域巧妙地解決了邊界問題。

UTF-16將代理區進一步劃分紅兩部分。0xD800~0xDBFF,稱爲高半代理(leading surrogate)。0xDC00~0xDFFF分區, 稱爲低半代理(trailing surrogate)。

不可貴到,高半代理範圍含有0xDBFF-0xD800+1=0x0400=2^10個碼位,低半代理範圍也有0x0400=2^10個碼位。

除去基本平面已經分配存儲方案的碼位後,輔助平面還剩下0x10FFFF-0x010000+1 = 0x0F0000,約爲2^20個碼位。這些碼位還須要分配存儲方案。假如將這些碼位值所有存儲成二進制數,須要至少20位來存儲,至少也須要四個字節。

UTF-16的作法是,將這四個字節的前兩個字節映射到高半代理表示的範圍,後兩個字節映射到低半代理的範圍。高半代理和低半代理總共能表示2^10*2^10=2^20個數,恰好能爲每一個輔助平面還剩下的2^20個碼位分配存儲方案。

這樣,按兩個字節兩個字節讀取的方式判別,假如讀到的值不在代理區內,就證實這就是一個BMP內的字符。假如讀到的值位於前導代理範圍內,證實這是一個四字節輔助字符的開頭,後面兩個字節是這個字符的延續。假如讀到的值位於後導代理範圍內,證實前面兩個字節也屬於這個輔助字符的一部分。

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

有了具體的存儲方案,unicode在制定標準時就只須要爲字符分配抽象的碼位了,具體的存儲由採用UTF-8仍是UTF-16方案來定,極大方便了編碼的制定。

所以,下面討論emoji表情編碼時不須要討論其存儲方案,只須要討論其邏輯層次上的Unicode編碼。

emoji表情的unicode編碼

emoji表情你們應該也比較熟悉了。像經常使用的🐂🍺用語就是emoji表情組成的。咱們再來談談在unicode對於emoji表情的編碼。這裏也要注意,Unicode只是規定了 emoji 的碼位和含義,以及用文字指導它們表明的表情長什麼樣,並無規定它的具體樣式。渲染的工做則由各個系統本身實現。若是用戶的系統沒有實現某個emoji表情的渲染,就會顯示成一個空方框。

在MAC中,輸入ctrl + cmd + 空格後在彈出的面板裏添加unicode代碼表就能夠看見每一個unicode碼位對應的字符了。其中能夠很是方便地查詢到字符對應到編碼值。以下圖所示。

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

能夠發現簡單的笑臉對應的是Unicode的一個碼位,但一個蒸餾器對應了兩個碼位,一個金髮男子表情居然對應了五個碼位。咱們看到,一個emoji表情也是變長存儲的,並且一個表情可佔用多個碼位組成。那麼這些表情的渲染又有什麼規則呢?


要解答這個問題,須要閱讀unicode官方介紹emoji表情的文檔。

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

關於emoji表情涉及到許多定義,見其中1.4節。其中有這樣的定義

text_presentation_selector := \x{FE0E}

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

text_presentation_sequence := emoji_character text_presentation_selector

這個定義代表,加上了這個U+FE0E修飾的emoji字符會構成一個文本表示序列。因此,這個text_presentation_selectorU+FE0E是用來修飾前面的emoji表情,指定其展現方式的。

不過筆者使用的macbook對這種文本表示emoji的表情尚未支持,使用上面的輸入法添加U+FE0E選擇符後並無成功渲染出emoji的文本表示形態。

相似的,還存在定義

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

加上了U+FE0F修飾的emoji字符會構成一個emoji表示序列。

例如這個蒸餾器表情就是由U+FE0F修飾U+2697產生的,能夠利用剛纔提到的mac自帶輸入法嘗試一下。

輸入查找Unicode編碼U+2697輸出獲得⚗這個小蒸餾器符號

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

再連在前面輸入的U+2697⚗符號後面後面輸入這個修飾符就獲得了⚗️這個emoji表情。

由此,咱們理解了U+FE0F的做用,至關於一個修飾符,在渲染文字時若是遇到了U+FE0F就和前面的字符組合一下,改爲渲染成一個emoji表情的形式。這樣就能夠理解上面的蒸餾器爲何佔用了二個Unicode碼位了。自己這個表情就是由其餘字符加上修飾符組合而成的。

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

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

意思是,一個做爲base的表情加上一個modifier修飾符後會構成一個emoji修飾符序列。有些表情會具備base屬性,有些則具備modifer屬性。下面來看個例子。

在輸入法中找到U+1F471號字符,單獨輸出。 

👱 

再找到U+1F3FF號字符,單獨輸出

此次再將兩個表情連在一塊兒輸出,結果變成了一個

相似還有其餘的組合規則,如國旗,鍵盤等都有各自的組合規則,也是用具備不一樣屬性的unicode字符組合而成。詳情可翻閱文檔查閱。

另外,介紹Emoji表情編碼還不得不提到一個特殊字符ZWJ,全程zero-width joiner,意思是零寬度連字符,佔用碼位U+200D。這個字符不是爲emoji單獨服務的。用於插入在某些語言的字符中,使左右的兩個字符連在一塊兒產生連字效果,合成單我的類可讀的字符。

具體到emoji表情中,某些單個顯示錶情也會由多個獨立的ZWJ連字合成獲得。

舉例

能夠看到,這個紅髮女子就是由U+1F469和U+1F9B0使用U+200D連字獲得的。下面咱們來輸入看看這兩個字符分別表明什麼。

U+1F469 

👩

U+1F9B0

這代表,U+1F469表明的是一個女子,而U+1F9B0單個表情表明的是頭髮的顏色,利用U+200D組合到一塊兒,就獲得了紅髮女子這個表情。

有了這些組合方式的基礎,再回頭看前面的金髮黑人男子就不難理解爲何它由那麼多Unicode字符組成了。本質也是經過前面所述的三種表示規則組成獲得的。你們能夠用輸入法每一個字符依次自行輸入一遍金髮黑人男子表情,就能理解其合成的方式。

總結與展望

限於時間與篇幅,本文僅僅回顧ASCII標準,並介紹了Unicode和UTF-八、UTF-16方案間的關係,各自是如何存儲的,最後介紹了Unicode中emoji表情的構成規則。

掌握這些編碼的通用基礎知識,是後續學習各語言各平臺對字符串處理規則的基礎。每一個語言存儲字符串時採用UTF-8仍是UTF-16都會有所不一樣,視每一個語言、每一個編譯器的具體實現而定。

特別是對於emoji表情,因爲其組成規則複雜,多是由多個Unicode字符組合而成的。因此在處理含有emoij表情的字符串時,使用索引、統計長度和其餘字符串比較運算都須要一些算法支持。iOS開發使用Swift語言時,emoji表情能做爲單個的字符進行索引,特性強大,但同時也所以形成了Swift語言的字符串使用起來並不方便。

後續的文章會逐步介紹Swift語言對字符串的實現機制,研讀swift是如何處理含義emoji表情的字符串。



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

回覆「加羣」與大佬們一塊兒交流學習~

點這,與你們一塊兒分享本文吧~

本文分享自微信公衆號 - 前端自習課(FE-study)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索