深入了解Emoji(三) —— Emoji详解

作者 : 开心源码 本文共4378个字,预计阅读时间需要11分钟 发布时间: 2022-05-12 共185人阅读

深入了解Emoji(一) —— 字符集,字符集编码
深入了解Emoji(二) —— 字节序和BOM

Emoji字符是Unicode字符集中一部分. 特定形象的Emoji表情符号对应到特定的Unicode字节。常见的Emoji表情符号在Unicode字符集中的范围和具体的字节映射关系, 可通过Emoji Unicode Tables查看到。

问题引申

首先来看看我遇到的问题:

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 = 2flag emoji length = 4portrait emoji length = 7family emoji length = 11

有没有觉得很奇怪,按我们之前所说,一个emoji表情应该也是属于一个字符,占据着Unicode的一个码点,为什么会出现2、4甚至是7、11个字符长度的情况呢?我们去看看String.length()的源码:

public int length() {        return value.length >> coder();    }

coder()这个方法是判断当前的编码获取相应的值,默认是UTF-16,值为1,由于Java内部的默认编码是UTF-16。也就是说,当字符的码点在辅助平面时,String.length()的实现方式会将其判断为长度为2。Emoji表情所有的码点都在辅助平面上,那就解释了第一个,为什么长度为2,那大于2的那些又是怎样回事呢?这就涉及到Unicode的一个很重要的特性:组合字符

组合字符

Unicode 包含一个系统,可以合并多个编码点,动态组合字符。此系统用各种方式添加灵活性,而不引起编码点的巨大组合膨胀。
例如,在欧洲语言中,组合标记出现在变音符和字母的使用中。 Unicode 支持各种各样的变音符号,包括尖音符号的和重音符号、元音变音符号、变音符号等等。所有这些变音符可以被使用在任何字母表的字母中。事实上,多个变音符号可以被使用在一个字母上。

假如 Unicode 试图为每个字母组合或者变音符组合分配一个独立的编码点,事情会变得无法控制。相反,动态组合系统可以让你构造你想要的任何字符,通过以一个基础编码点(字母)开始而后附加额外的编码点,被称作“组合标识”,来指定变音符。当一个文字渲染器看到字符串中有这样的序列时,它会自动堆叠变音符到基础字母的上面或者下面来造出一个组合字符。

例如,带重音的字符“á” 会被表示成由两个编码点组成的字符串: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组成的笑脸

  • 双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个长度。

变量选择器样式比照
而在VS-16的基础上,还有一种键帽序列(KeyCap Sequence),这类 emoji 序列是将数字(0-9),* 与 # 通过一个 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个长度。
键帽序列组成的Emoji
另外, 还有少量控制型的Emoji, 可以对人体肤色进行改变,改变对象仅限于”表示人身体部位的Emoji”。目前定义了五种修饰字符,分别表示颜色的由深及浅,它们分别是: 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 10、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。

SWJ
当然不局限于家庭人物,包括职业,运动等许多都是用这种方式组成的
SWJ职业
SWJ运动

标准码协会利用ZWJ字符序列的方式(可以跨多平台使用),使得各IT公司可以轻易地进行开发,不过同时也有个显著的问题。Emoji表情和ZWJ字符串不需要标准码协会批准即可以建立并在自有平台上使用。即便在不支持ZWJ的老版本中,最多也是显示两个或者是两个以上独立的表情,增加新的代码不会破坏其余或者是出现丑陋的问号块。

不需要耗一个月甚至一年的时间等候审批,可以使表情开发变得更快,苹果或者是谷歌可以自主增加标志或者处理问题,而不会影响与其余平台的兼容。另一方面,这也使以ZWJ序列排列出的体现被跨平台支持,但事实上却没能被支持。各个平台都在开发属于自己的表情,会导致不同平台间的符号不兼容,比方字符长度的问题,在IOS系统上,一个Emoji表情发送到Android手机上,可能会出现4、5个,假如在有长度限制的条件下,便可能会出现截断的问题。

Emoji的碎片化

标准码协会提供所有表情符号的名称和简单的图片,但任何Emoji文章展现,你通过手机和电脑看起来也有轻微的区别。不同的操作系统和程序开发者都想通过不同的emoji表情来达到更美观,而不是用统一的通用字符集。好像我们的截图,同一个Emoji表情码,有不同的平台上有各式各样的体现形式。又由于SWJ的存在,导致各个平台有属于自己的一套表情,这就导致了Emoji的混乱,这点其实跟Unicode的“统一”多多少少是有点冲突的。但不论怎样说,Emoji都是一个非常伟大且成功的发明。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 深入了解Emoji(三) —— Emoji详解

发表回复