作者:Hsusue
链接:https://juejin.im/post/5dc3b9a46fb9a04a95289a84(点击尾部阅读原文前往)
Emoji 简介
绘文字(日语:絵文字/えもじ emoji)是日本在无线通信中所使用的视觉情感符号,绘指图画,文字指的则是字符,可用来代表多种表情,如笑脸表示笑、蛋糕表示食物等。在中国大陆,emoji通常叫做“小黄脸”,或者直称emoji 在NTTDoCoMo的i-mode系统电话系统中,绘文字的尺寸是12x12 像素,在传送时,一个图形有2个字节。Unicode编码为E63E到E757,而在Shift-JIS编码则是从F89F到F9FC。基本的绘文字共有176个符号,在C-HTML4.0的编程语言中,则另增添了76个情感符号。最早由栗田穰崇(Shigetaka Kurita)创作,并在日本网络及手机用户中流行。自苹果公司发布的iOS 5输入法中加入了emoji后,这种表情符号开始席卷全球,目前emoji已被大多数现代计算机系统所兼容的Unicode编码采纳,普遍应用于各种手机短信和社交网络中。
以上引用来自百度百科,提到“一个图形有2个字节,Unicode 编码范围为E63E到E757”。但人的创造性是无穷的,限定的区域无法满足人们表达的欲望。所以 Emoji 并不限定于2个字节,人类针对这个问题制定了越来越多的规则。
但限定的规则总是伴随着两个问题——兼容性以及扩展性,如何过滤掉不支持的 Emoji,如何扩展更多的 Emoji。
核心问题就是 Emoji 编码规则是怎样的。
Emoji 编码
MAC 下查看 Unicode 编码 和 UTF-8 编码
按 ctrl + cmd + 空格,展示 Emoji 键盘,点击右上角。
点击左上角设置 - 自定列表。
选中 Unicode 。
现在我们就可以选中 Emoji 查看 Unicode 和 UTF-8 码。
可以看到这个狗东西,Unicode 书写成 U+1F436,UTF-8 占用了四个字节。
如果你点多几个 Emoji 来看,会发现事情并不简单。
中国国旗占了两个 Unicode代码块,UTF-8 占了八个字节。
gay 里 gay 气的 Emoji UTF-8 居然占了...不想数,Unicode 的代码点(后面会提到这概念) 也不止一个。
更有趣的是,晒黑后字节数也不一样。
那 Unicode 和 UTF-8 是什么呢?要了解这个问题,首先要追溯到 ASCII。
ASCII
ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符。
一个字符的ASCII码占用存储空间为1个字节。所以理论上能表示 2^8 = 256 个字符。
标准ASCII码也叫基础ASCII码,只用到了后7位,即128个字符,剩下最高位(b7)用于校验。
虽然128个足以表示英语中的所有日常字符,但是例如法语注音符号é等就不足以表示,所以一些欧洲国家也用了最高位代表另外的符号。
总的来说,ASCII码 0~127 表示的符号都是一样的,128~255 表示的可能有所差别。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。
至于博大精深的汉语,文字就更多了,1个字节不足以表示所有的汉字,所以 GBK 编码等采用了2个字节。
同理,人的创造性是无穷的,Emoji、花漾字等,所以也诞生了许多别的编码方式,所需的字节数会越来越多。
但无论如何,各种编码方式 0~127 代表的字符都建议与标准 ASCII 码中一样,达到兼容的效果。
Unicode
Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。——百度百科
Unicode码:Unicode码是一种国际标准编码,采用二个字节编码,与ASCII码不兼容。——百度百科
可以看到,Unicode 包括字符集、编码方案等;采用两个字节编码。
Unicode 的一些概念
字符集、码点
字符集(unicode)是一张码表,它规定了文字与数字的一一对应关系。
在设计字符集时,首先要决定所需字符的数目,并确定所需字符的清单。根据字符的数目,可以设定整数值的上限,这个整数范围称为编码空间(code space)。在Unicode标准中,编码空间的整数范围是从0到10FFFF(编码空间其中的一个特定整数称为一个码点(code point)),共1,114,112个可用的码点。
然后,为字符清单中的每个字符指定一个整数值,也就是一个码点。这样就得到一个字符集,称作编码字符集(Coded Character Set)。
在书写 Unicode 字符的码位时,通常会在前面加一个前缀 U+,而数值部分会用 4 位到 6 位十六进制数值表示。如字符“A”在 Unicode 中的码位为 U+0041。
平面
Unicode 编码空间的范围为0到10FFFF,可以被划分为字符平面(planes of characters),一共有17个平面,每个平面包含2^16,64K个码点。
平面 0 (U+0000 - U+FFFF) 被称为基本多语言平面 Basic Multilingual Plane (BMP),也称为第零平面, 其中包含了那些频繁使用的字符。
平面 1 (U+10000 - U+1FFFF) 被称为增补多语言平 Supplementary Multilingual plane (SMP),也称为第一平面。其中包含了一些不常使用的字母系统,如 Deseret。
平面 2 (U+20000 - U+2FFFF) 被称为增补表意字符平面 Supplementary Ideographic Plane (SIP),也称为第二平面。其中包含的事表意字符(如汉字),这其中的大多数字符是不常使用的。
平面 14 (U+E0000 - U+EFFFF) 被称为增补专用平面 Supplementary Special-purpose Plane(SSP)。
平面 15 和 16 (U+F0000 - U+10FFFF) 是 Private Use planes。加上 U+E000 - U+F8FF 就构成了 Unicode 的 Private Use Area(PUA)。这部分区域是 Unicode 为用户保留的,Unicode 不会给这些码位指定字符,应用可以在这块区域添加自己的字符。
其它的平面都还没被使用。
Unicode 转换格式:UTFs
UTF是“Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。
我们应该见过 UTF-8、UTF-16、UTF-32 的编码。它们占用的字节数不是固定的。举个例子。
UTF-8 通常使用一至四个字节为每个字符编码,但最多可用到6个字节。
128 个 ASCII 字符(Unicode 范围由 U+0000 至 U+007F)只需一个字节,带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及马尔代夫语(Unicode 范围由 U+0080 至 U+07FF)需要二个字节,其他基本多文种平面(BMP)中的字符(CJK属于此类-Qieqie注)使用三个字节,其他 Unicode 辅助平面的字符使用四字节编码。
UTF-8的编码规则很简单, 只有两条:
对于单字节的符号, 字节的第一位设为0, 后面7位为这个符号的unicode码. 因此对于 英语字母, UTF-8编码和ASCII码是相同的.
对于n字节的符号(n>1), 第一个字节的前n位都设为1, 第n+1位设为0, 后面字节的前 两位一律设为10. 剩下的没有提及的二进制位, 全部为这个符号的unicode码.
如下表:
字节数
表达
Unicode 符号范围
1 | 0xxxxxxx | 0000 0000 - 0000 007F |
2 | 110xxxxx 10xxxxxx | 0000 0080 - 0000 07FF |
3 | 1110xxxx 10xxxxxx 10xxxxxx | 0000 0800 - 0000 FFFF |
4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 0001 0000 - 0010 FFFF |
5 | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx | 0020 0000 - 03FF FFFF |
6 | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx | 0400 0000 - 7FFF FFFF |
稍微解释一下。
UTF-8 1字节用来表示128个 ASCII 字符,所以 Unicode 符号范围位 0 - 7F,即 0 - 127。其他类比。
UTF-8中可以用来表示字符编码的实际位数最多有31位,即6字节中所有x的数目。
Unicode 和 UTF-8 的转换
以"严"举例。unicode 为 4E25(1001110 00100101)。
根据上表, 可以发现4E25处在第三行的 范围内(0000 0800 - 0000 FFFF), 因此"严"的UTF-8编码需要三个字节, 即格式是 "1110xxxx 10xxxxxx 10xxxxxx"。
然后, 从"严"的最后一个二进制位开始, 依次从后向前 填入格式中的x, 多出的位补0. 这样就得到了, "11100100 10111000 10100101", 转换成十六进制就是E4B8A5.