深刻理解Emoji(一) —— 字符集,字符集編碼 深刻理解Emoji(二) —— 字節序和BOM數據庫
Emoji字符是Unicode字符集中一部分. 特定形象的Emoji表情符號對應到特定的Unicode字節。常見的Emoji表情符號在Unicode字符集中的範圍和具體的字節映射關係, 可經過Emoji Unicode Tables查看到。bash
注:本篇文章在不一樣平臺下觀看效果會不同app
首先來看看我遇到的問題:ui
val smile = "😀"
print("smile emoji length = ${smile.length}")
val flag = "🇨🇳"
print("flag emoji length = ${flag.length}")
val portrait = "👩🏽🦳"
print("portrait emoji length = ${portrait.length}")
val family = "👨👩👧👧"
print("family emoji length = ${family.length}")
複製代碼
輸出結果爲:編碼
smile emoji length = 2
flag emoji length = 4
portrait emoji length = 7
family emoji length = 11
複製代碼
有沒有以爲很奇怪,按咱們以前所說,一個emoji表情應該也是屬於一個字符,佔據着Unicode的一個碼點,爲何會出現二、4甚至是七、11個字符長度的狀況呢?咱們去看看String.length()的源碼:spa
public int length() {
return value.length >> coder();
}
複製代碼
coder()
這個方法是判斷當前的編碼獲取相應的值,默認是UTF-16,值爲1,由於Java內部的默認編碼是UTF-16。也就是說,當字符的碼點在輔助平面時,String.length()
的實現方式會將其判斷爲長度爲2。Emoji表情全部的碼點都在輔助平面上,那就解釋了第一個,爲何長度爲2,那大於2的那些又是怎麼回事呢?這就涉及到Unicode的一個很重要的特性:組合字符操作系統
Unicode 包含一個系統,能夠合併多個編碼點,動態組合字符。此係統用各類方式增長靈活性,而不引發編碼點的巨大組合膨脹。 例如,在歐洲語言中,組合標記出如今變音符和字母的使用中。 Unicode 支持各類各樣的變音符號,包括尖音符號的和重音符號、元音變音符號、變音符號等等。全部這些變音符能夠被使用在任何字母表的字母中。事實上,多個變音符號能夠被使用在一個字母上。code
若是 Unicode 試圖爲每一個字母組合或變音符組合分配一個獨立的編碼點,事情會變得沒法控制。相反,動態組合系統可讓你構造你想要的任何字符,經過以一個基礎編碼點(字母)開始而後附加額外的編碼點,被稱做「組合標識」,來指定變音符。當一個文字渲染器看到字符串中有這樣的序列時,它會自動堆疊變音符到基礎字母的上面或下面來造出一個組合字符。cdn
例如,帶重音的字符「Á」 會被表示成由兩個編碼點組成的字符串:U+0041
「A」 拉丁大寫字母 a 加上 U+0301
「◌́」組合尖音符號。這個字符串自動被渲染成單個字符:「Á」。對象
有時候咱們會看到某些人的簽名中有很奇怪的字符,其實他們就是利用了組合字符。好比Á́́ 就是多添加了幾個尖音符號:U+0041U+0301U+0301U+0301,是否是感受挺有意思🤣
如上所見,Unicode 包含多種狀況,用戶認爲的一個「字符」 事實上底下可能由多個編碼點組成。Unicode 使用「字位簇」的概念來表示這種狀況。一個由一個或多個編碼點組成的字符串構成一個 「用戶感知的字符」。
UAX #29 爲字位叢定義了精確的規則。它大約是 「一個基本的編碼點接着任意數量的組合標記」,可是真實的定義有點複雜;它包含了朝鮮語字母,和 emoji ZWJ 序列。
字位簇主要被用在文本編輯:它們對光標和文本選擇來講是最明顯的單元。使用字位簇,確保在複製和粘貼文本時不會忽然丟掉一些符號,同時左右方向鍵也老是以一個可見字符的距離移動,等等。
另外一個用到字位簇的地方是,執行字符串長度限制——好比在數據庫域中。其實,底層的限制多是相似 UTF-8 中的字節長度之類的東西,你不能簡單的經過截斷字節的方式來限制長度。至少,你得 「捨去」 最近的編碼點;但更好的是,捨去最近的字位簇。除此之外,你能夠經過捨棄它的一個注音符號破壞一個字符,中斷一個 jamo 序列或 ZWJ 序列。
##Emoji組合規則 如今,咱們知道了一個Emoji表情可能由多個碼點組成,這些碼點都遵循着必定的規則來組合成不一樣的Emoji表情,咱們來看下幾種常見的規則:
#####單Unicode 最基本的Emoji表情,碼點位於輔助平面上。在UTF-16下經過String.length()
會被判斷爲2個長度,可使用String.codePoints()
經過碼點數來獲取正確的長度。
#####雙Unicode 最具表明性的就是旗幟序列(Flag Sequence),這類 Emoji 串是經過兩個地域指示符(regional_indicator)組合的方式來表示一個國家的國旗。總共有 26 個地域指示符(U+1F1E6
~U+1F1FF
),每一個指示符又對應於一個英文字母含義,例如 U+1F1E8
爲地域指示符 C, U+1F1F3
爲地域指示符 N。這些指示符兩兩組合表示一個國旗CN即中國國旗(🇨🇳),在不支持Emoji5.0的系統上,會被顯示爲兩個字母Emoji表情(🇨 🇳)。並非 26 x 26 種組合是所有合法的,合法的 Flag Sequence 只有 256 種。這種Emoji表情經過String.length()
會被判斷爲4個長度。
#####變量選擇器 在衆多Emoji中, 有一些特殊的Emoji 並無顯示的樣式, 只是起到了控制的做用。這些控制型的Emoji 與基礎Emoji 出如今一塊兒, 能夠展現更多的樣式。好比 變量選擇器
變量選擇器-15(VARIATION SELECTOR-15, 簡寫VS-15): <U+FE0E
>, 做用是讓基礎Emoji 變成更接近文本樣式(text-style); 變量選擇器-16(VARIATION SELECTOR-16, 簡寫VS-16): <U+FE0F
>, 做用則是讓基礎Emoji 變成更接近Emoji樣式(emoji-style).
VS-15 和 VS-16 加在基礎Emoji字符的後面, 能夠起到控制做用(前提是必須系統支持, 不然會被忽略)。在UTF-16下經過String.length()
會被判斷爲2個長度。
U+20E3
字符轉換爲鍵帽的樣式。因爲這種樣式要求必須以 emoji 風格展現,全部會在序列中添加樣式限制 U+FE0F。例如 U+0023 U+FE0F U+20E3 的 emoji 樣式便是 #️⃣,U+0030 U+FE0F U+20E3 的 emoji 樣式便是 0️⃣。其它與此相似。在UTF-16下經過
String.length()
會被判斷爲3個長度。
U+1F3FB
~
U+1F3FF
(🏻..🏿)共五個, 分別簡稱爲: FITZ-1-2, FITZ-3, FITZ-4, FITZ-5, FITZ-6。例如,U+270D(✍️) 就是一個能夠被修飾的 emoji 字符,那麼它被
U+1F3FF
修飾後就會變成
U+270D U+1F3FF
(✍️🏿)。
上面說到,經過一些特定的Emoji組合,能夠結合出不一樣膚色的表情,在增長Emoji的豐富度的同時,不須要增長過多的碼點。那性別,職業呢?是否是也能夠用這種方式,答案是確定的,只不過實現的方法有點不同。
一般,每個emoji表情都是由特定的字符來展示的,新創造一個emoji表情意味着要新建一個符號來與之關聯。以膚色和性別爲例,標準碼協會提出更多創造性的解決方案,好比選擇將多個代碼結合在一塊兒來建立一個新表情。
不一樣性別的表情所表明的職業如何來展示的呢?以一個標準的「男性」或是「女性」表情再添加個表明職業的表情,就能展示「男性」某職業或女性某職業這樣一個表情,而不是兩個表情。這種特殊不可見的排列方式被稱爲「無縫鏈接」(「Zero-width joiner,即ZWJ」)。在iOS 十、Android N平臺支持這種組合表情,看到ZWJ就知道顯示一個表情而不是分離的兩個。
U+200D
即是鏈接這些表情的字符。例如,U+1F468 U+200D U+1F469 U+200D U+1F467
(👨👩👧) 這個 emoji 表示家庭即由三個emoji字符,U+1F468
(👨), U+1F469
(👩), U+1F467
(👧) 經 ZWJ 鏈接而成的。長度爲8,而上面問題裏提到的👨👩👧👧,能夠看到多了一個鏈接符和一個長度爲2的基本Emoji表情,因此打印出來是11。
標準碼協會利用ZWJ字符序列的方式(能夠跨多平臺使用),使得各IT公司能夠輕易地進行開發,不過同時也有個明顯的問題。**Emoji表情和ZWJ字符串不須要標準碼協會批准就能夠創建並在自有平臺上使用。**即便在不支持ZWJ的老版本中,最多也是顯示兩個或是兩個以上獨立的表情,添加新的代碼不會破壞其餘或是出現醜陋的問號塊。
不須要耗一個月甚至一年的時間等候審批,可使表情開發變得更快,蘋果或是谷歌能夠自主添加標誌或解決問題,而不會影響與其餘平臺的兼容。另外一方面,這也使以ZWJ序列排列出的表現被跨平臺支持,但事實上卻沒能被支持。各個平臺都在開發屬於本身的表情,會致使不一樣平臺間的符號不兼容,好比字符長度的問題,在IOS系統上,一個Emoji表情發送到Android手機上,可能會出現四、5個,若是在有長度限制的條件下,即可能會出現截斷的問題。
標準碼協會提供全部表情符號的名稱和簡單的圖片,但任何Emoji文章展現,你經過手機和電腦看起來也有輕微的區別。不一樣的操做系統和程序開發者都想經過不一樣的emoji表情來達到更美觀,而不是用統一的通用字符集。如同咱們的截圖,同一個Emoji表情碼,有不一樣的平臺上有各式各樣的表現形式。又由於SWJ的存在,致使各個平臺有屬於本身的一套表情,這就致使了Emoji的混亂,這點其實跟Unicode的「統一」多多少少是有點衝突的。但無論怎麼說,Emoji都是一個很是偉大且成功的發明。