我在虎嗅上看过一篇关于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在中间被截断的问题。
解决方案也有多种: