我在虎嗅上看過一篇關於Emoji的趣聞, 特別有意思, 在這裏跟你們分享一下。裏面提到了Emoji是怎麼誕生的。html
1999年先後,日本一個名叫慄田穰崇的年輕人,和許多直男同樣, 給女朋友發的短信常常會被誤解。好比,「知道了」被解讀成「生氣了」、「不耐煩了」,隨後引起冷戰。 因而少年慄田想:「若是能在文字裏插入一些表情符號來表達感情,你們應該會須要吧!」
原始的Emoji就這麼誕生了。
Emoji極大地豐富了咱們的生活和通信交流。Emoji誕生自程序員,但反過來對程序員也形成過一些困擾。
尤爲對於面向C端的產品開發者, 用戶愈來愈習慣於輸入Emoji, 所以處理字符時遇到Emoji也只會愈來愈頻繁。python
Emoji字符是Unicode字符集中一部分. 特定形象的Emoji表情符號對應到特定的Unicode字節。
常見的Emoji表情符號在Unicode字符集中的範圍和具體的字節映射關係, 可經過Emoji Unicode Tables查看到。mysql
有意思的是, 在Emoji Unicode Tables表中,還給出了同一個Emoji表情在不一樣系統中的字體(是字體沒錯, Emoji的樣式可經過字體文件改變)。git
關於Emoji的最權威資料, 能夠在Unicode® Emoji Charts上查閱到。
截止我寫這篇文章的時刻, Emoji Charts 的最新版本是v3.0, v4.0還只是處於Beta階段。程序員
題外話補充一點: Unicode是一種字符編碼方法,它是由國際組織設計,能夠容納全世界全部語言文字的編碼方案。
咱們所知道的UTF-八、UTF-16等編碼, 是對Unicode的不一樣實現方式。
若是要深刻了解更多關於ASCII、Unicode、UTF-八、gb23十二、gbk等編碼的相關知識,在這裏強烈推薦幾篇文章,講得很是好。github
在衆多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).sql
VS-15 和 VS-16 加在基礎Emoji字符的後面, 能夠起到控制做用(前提是必須系統支持, 不然會被忽略)。數據庫
用一段Python代碼來演示該例子:編程
# -*- coding: utf-8 -*- # more info to see https://en.wikipedia.org/wiki/Emoji # 符號分別是上圖(截圖自wiki)中的符號, 最後再加上一個「狗」的Emoji sample_list = [u'\u2139', u'\u231B', u'\u26A0', u'\u2712', u'\u2764', u'\U0001F004', u'\U0001F21A', u'\U0001f436', ] # 輸出原樣式 for code in sample_list: print code, print print '-' * 20 # 後面加上VS-15 for code in sample_list: print (code + u'\uFE0E'), print print '-' * 20 # 後面加上VS-16 for code in sample_list: print (code + u'\uFE0F'),
其輸出以下圖, 第一行是原樣式,第二行是加上VS-15後的樣式,第三行是加上VS-16後的樣式:
另外, 還有一些控制型的Emoji, 能夠對人體膚色進行改變,改變對象僅限於"表示人身體部位的Emoji".
它們分別是: <U+1F3FB>
– <U+1F3FF>
共五個, 分別簡稱爲: FITZ-1-2, FITZ-3, FITZ-4, FITZ-5, FITZ-6.
還有一個特殊的控制符: <U+200D>
(ZERO WIDTH JOINER, 簡寫ZWJ), 起到了鏈接Emoji的做用, 從而將多個Emoji變成一個Emoji來顯示. 一樣,前提是必須系統支持, 不然會被忽略.
使用Python代碼演示 FITZ-*
和 ZWJ
:
# -*- coding: utf-8 -*- # more info to see https://en.wikipedia.org/wiki/Emoji # man_list 分別是: 男孩 女孩 男人 女人 man_list = [u'\U0001F466', u'\U0001F467', u'\U0001F468', u'\U0001F469'] # skin_color_list 分別是: 空字符串,表示默認 白種人 -->(不斷加深膚色) 黑種人 skin_color_list = ['', u'\U0001F3FB', u'\U0001F3FC', u'\U0001F3FD', u'\U0001F3FE', u'\U0001F3FF', ] for man in man_list: for color in skin_color_list: print (man + color), print print '-' * 20 # Emoji的鏈接符<U+200D> (英文名爲: ZERO WIDTH JOINER, 簡寫ZWJ ) # 若是系統支持: 鏈接(男人 + ZWJ + 女人 + ZWJ + 女孩) print u'\U0001F468' + u'\u200D' + u'\U0001F469' + u'\u200D' + u'\U0001F467' # 若是系統不支持: 鏈接(狗 + ZWJ + 貓 + ZWJ + 老鼠) print u'\U0001f436' + u'\u200D' + u'\U0001f431' + u'\u200D' + u'\U0001f42d'
其輸出以下圖:
以上內容參考自維基百科
對Emoji 的介紹到該小節結束, 下面內容是一些關於實際中可能遇到的技術問題的解決方法。
使用MySQL存儲Emoji, 只須要數據表的字符集爲utf8mb4
便可, 即CHARSET=utf8mb4
.
若是想要知道你的MySQL數據庫是否支持utf8mb4
編碼, 可經過show charset;
輸出當前安裝的MySQL所支持的全部字符集, 查看輸出中是否包含有utf8mb4
.
另外, 有一些比較老的業務, 可能一開始設計時沒考慮到須要支持Emoji, 那就須要修改數據庫或數據表的字符集.
查看MySQL說支持的全部字符集 mysql> show charset; 查看某張表當前的字符集 mysql> show create table <table_name>; 建立默認字符集爲utf8mb4的數據庫.在該數據庫中,若是建立表時是不指明字符集,則默認utf8mb4. mysql> create database default charset utf8mb4; 建立字符集爲utf8mb4的表, 數據庫的默認字符集非utf8mb4也沒問題. mysql> create table `<table_name>` (Column定義, Column定義, ...) DEFAULT CHARSET=utf8mb4; 修改已存在的數據庫的字符集 mysql> alter database <db_name> default charset = utf8mb4; 修改已存在的表的字符集 mysql> alter table <table_name> default charset = utf8mb4;
很惋惜, Emoji的範圍並無明確的定義。正如上面提到了,Emoji Charts目前最新版本是v3.0, 將來Emoji的範圍還會不斷擴大。並且Emoji 在Unicode的分配中並非連續的區間。
因此, 在這裏我只能給出一個可行的匹配區間, 儘量涵蓋了基本常見的Emoj。
該匹配區間中會包含一些未定義的字符, 可能在某些系統會有定義,可是在另外的系統中並無定義。畢竟Emoji是商業的產物。
該匹配規則區間參考了emoji-data.txt 和 Unicode® Technical Report #51, 以下:
<U+1F300> - <U+1F5FF> # symbols & pictographs <U+1F600> - <U+1F64F> # emoticons <U+1F680> - <U+1F6FF> # transport & map symbols <U+2600> - <U+2B55> # other
下面使用Python代碼來演示如何使用正則表達式替換(或找出)字符串中的Emoji:
# -*- coding: utf-8 -*- import re try: # Wide UCS-4 build myre = re.compile(u'[' u'\U0001F300-\U0001F64F' u'\U0001F680-\U0001F6FF' u'\u2600-\u2B55]+', re.UNICODE) except re.error: # Narrow UCS-2 build myre = re.compile(u'(' u'\ud83c[\udf00-\udfff]|' u'\ud83d[\udc00-\ude4f\ude80-\udeff]|' u'[\u2600-\u2B55])+', re.UNICODE) sss = u'I have a dog \U0001f436 . You have a cat \U0001f431 ! I smile \U0001f601 to you!' print myre.sub('[Emoji]', sss) # 替換字符串中的Emoji print myre.findall(sss) # 找出字符串中的Emoji
輸出以下:
I have a dog [Emoji] . You have a cat [Emoji] ! I smile [Emoji] to you! [u'\U0001f436', u'\U0001f431', u'\U0001f601']
上面例子中, 之因此使用try...except...
來處理代碼, 是考慮到 UCS-2 (Narrow UCS-2 build) 和 UCS-4 (Wide UCS-4 build) 的區別.
該Demo例子參考了stackoverflow上的精彩回答, 解答了我對此的困惑。
關於UCS-2和UCS-4的區別, 在上面提到的擴展閱讀程序員趣味讀物:談談Unicode編碼中有提到, 值得一看.
本文中使用到的示例代碼,能夠在個人github下載到。
在Python、JavaScript 這類編程語言中, 一箇中文字符的長度爲1,可是對大部分的Emoji(並不是所有), 取長度則是2。下面使用Python作演示。
以中文的"漢"字取長度爲例,取長度爲1:
>>>len(u'漢') 1
而對於Emoji,以<U+1f436>
(該Emoji是一隻萌萌的狗)爲例,取長度爲2:
>>>len(u'\U0001f436') 2
那麼, 這就存在一個隱患, 在對字符串進行截斷時可能從中間截斷, 致使該字符顯示爲亂碼, 甚至引起報錯。
下面例子中, 對字符串進行截取時,正好從<U+1f436>
的中間截斷了,出現了亂碼:
>>>u'這是一隻可愛的狗狗\U0001f436'.__len__() 11 >>>u'這是一隻可愛的狗狗\U0001f436'[0:10] 這是一隻可愛的狗狗???
實際場景中,對字符串進行截斷是很是常見的需求,並且字符串每每多是用戶高度自由的輸入內容, 那麼包含Emoji的可能性實際上是很高的。
一個具體的場景就是: 你正在開發了一款社交APP, 容許用戶保存文字記錄, 而後在應用的某個地方, 又須要顯示這些文字記錄的摘要,摘要只顯示用戶輸入的前100個字符, 超出部分用省略號表示。
這種狀況下,就不可避免的可能發生Emoji在中間被截斷的問題。
解決方案也有多種: