不要小看小小的 emoji 表情

前言

很久没更新了,最近事比较多,或许下个月就会恢复到正常的发文频次。java

这篇文章得从一个 emoji 表情开始,我以前开源的一个 IM 项目中有朋友提到但愿能够支持 emoji 表情传输。git

https://github.com/crossoverJie/cim/issues/12github

正好那段时间有空,加上这功能看着也比较简单准备把它实现了。数据库

<!--more-->app

但在真正实现时却发现没那么简单。编码


我首先尝试将一个 emoji 表情存入数据库看看:spa

果不其然的出错了,致使这个异常的缘由是目前数据库所支持的编码中并不能存放 emoji,那 emoji 表情究竟是个什么东西呢。翻译

本质上来讲计算机所存储的信息都是二进制 01emoji 也不例外,只要存储和读取(编解码)的方式一致那就能够准确的展现这个信息。debug

更多编解码的内容后文再介绍,这里先想一想如何快速解决问题。3d

存储 emoji

虽然说想要在 MySQL 中存储 emoji 的方式也有好几种,好比能够升级存储字符集到能够存放 emoji ,但这种须要 MySQL 的版本支持。

因此更保险的方式仍是在应用层解决,好比咱们是否能够将 emoji 当作字符串存储,只是显示的时候要格式化为一个 emoji 表情,这样对于全部的数据库版本均可兼容。

因而咱们这里的需求是一个 emoji 表情转换为字符串,同时还得将这个字符串转换为 emoji。

为此我在 GitHub 上找到了一个库,它能够方便的将一个 emoji 转换为字符串的别名,同时也支持将这个别名转换为 emoji

https://github.com/vdurmont/emoji-java

@Test
    public void emoji() throws Exception{
        String str = "An :grinning:awesome :smiley:string &#128516;with a few :wink:emojis!";
        String result = EmojiParser.parseToUnicode(str);
        System.out.println(result);

        result = EmojiParser.parseToAliases(str);
        System.out.println(result);

    }

因此基于这个基础库最终实现了表情功能。

其实它本质上是本身维护了一个 emoji 的别名及它的 Unicode 编码(本质上是 UTF-16)的映射关系,再每次格式化数据的时候都会从这个表中进行翻译。

编码知识回顾

自此需求是完成了,但还有几个问题待解决。

  • Java 中是如何存储 emoji 的?
  • emoji 是如何进行编码的?

ASCII

在谈 emoji 以前很是有必要了解下计算机编码鼻祖的 ASCII 码。

你们如今都知道在计算机内部存储数据本质上都是二进制的 0/1,对于一个字节来讲有 8 位;每一位能够表示两种状态,也就是 0 或 1,这样排列组合下来,一个字节就能够表示 256(2∧8) 种不一样的状态。


对于美国来讲他们平常使用的英语只须要 26 个英文字母,再加上一些标点符号就足够用计算机来进行信息交流。

因而上个世纪 60年代定义了一套二进制与英文字符的映射关系,能够代表 128 个不一样的英文字符,也就是如今的 ASCII 码。

这样咱们就可使用一个字节来表示现代英文,看起来很是不错。

Unicode

随着计算机的发展,逐渐在欧洲、亚洲地区流行;再利用这套 ASCII 码进行信息交流显然是不行的,不少地区压根就不使用英文,并且也远超了 128 位字符(中文就更不用说了)。

虽然说一个字节在 ASCII 码中只用了 128 位,但剩下(258-128)的依然不足用用于描述其余语言。

这时若是能有一种包含了世界上全部的文字的字符集,每个地区的文字都在这个字符集中有惟一的二进制表示,这样便不会出现乱码问题了。

Unicode 就是来作这个的,截止目前 Unicode 已经收录了 10W+ 的字符,你所能使用的字符都包含进去了。

UTF-8

Unicode 虽然说包含了几乎全部的文字,但在咱们平常使用好像不多看到他的身影,咱们用的更多的仍是 UTF-8 这样的编码规则。

这也有几方面的缘由,好比说除开英文,其余大部分的文字都须要用 2 个甚至更多的字节来表示;若是统一都用 Unicode 来表示,那必然须要以占用字节最多的字符长度为标准。

好比汉字须要 2 个字节来表示,而英文只须要一个字节;这时就得规定 2 个字节表示一个字符,否则汉字就无法表示了。

但这样也会带来一个问题:用两个字节表示英文会使得第一个字节彻底是浪费的,若是一段信息全是英文那对内存的浪费是巨大的。


这时你们应该都能想到,咱们须要一个可变的长度的字符编码规则,当是英文时咱们就用一个字节表示,甚至能够彻底兼容 ASCII 码。

UTF-8 即是实现这个需求的,它利用两种规则能够表示一个字节以及多字节的字符。

大体规则以下:

  • 当第一个字节的第一位为 0 时便表示为单字节字符,此时和 ASCII 码一致,彻底兼容。
  • 当第一个字节为 1 时,有几个 1 便表明是几个字节 Unicode 字符。

这样即可根据字符的长度最大程度的节省存储空间。

固然还有其余的编码规则,好比 UTF-16UTF-32,平时用的很少,但本质上都和 UTF-8 同样,都是 Unicode 的不一样实现,也是用于表示世界上大部分文字的字符集。

Java 中的 emoji

如今来回到本次的主题,emoji

刚才说到 Unicode 包含了世界上大部分的字符,emoji 天然也不例外。

https://apps.timwhitlock.info/emoji/tables/unicode

这个表格中包含了全部的 emoji 以及它所对应的 Unicode 编码,同时也有对应的 UTF-8 编码的实现。

从图中也能够看出 emoji 表情用 UTF-8 表示时会占用 4 个字节,那在 Java 中它会是怎么存储的呢?

很简单,debug 一下就知道了。

在 Java 中也是经过 char 来存储 emoji 的,char 做为基本数据类型会占用 2 个字节;从刚才的图中能够看出,emoji 使用 UTF-8 会占用四个字节,这样很明显 char 是无法存储的,因此在这里实际上是使用 UTF-16 编码进行存储。

基于这个原理,咱们也能够本身实现将一个 emoji 表情转换为字符串,同时也可经过字符串转换为 emoji

总结

从此次研究 emoji 能够看出,任何一门基础知识都是应用的根基,在计算机行业尤其突出,但愿你们看完这篇能回忆起大学课堂被老师支配的恐惧😂。

随便提一下,相关源码可在这里查看:

https://github.com/crossoverJie/cim

相关文章
相关标签/搜索