前言

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

这篇文章得从一个 emoji 表情开始,我之前开源的一个 IM 项目中有朋友提到希望可以支持 emoji 表情传输。

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

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

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


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

果不其然的出错了,导致这个异常的原因是目前数据库所支持的编码中并不能存放 emoji,那 emoji 表情到底是个什么东西呢。

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

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

存储 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 😄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 可以看出,任何一门基础知识都是应用的根基,在计算机行业尤为突出,希望大家看完这篇能回忆起大学课堂被老师支配的恐惧

不要小看小小的 emoji 表情的更多相关文章

  1. 移动前端手机输入法自带emoji表情字符处理

    今天,测试给我提了一个BUG,说移动端输入emoji表情无法提交.很早以前就有思考过,手机输入法里自带的emoji表情,应该是某些特殊字符.既然是字符,那应该都能提交才对,可是为啥会被卡住呢?搜了一下 ...

  2. JS操作Unicode编码的emoji表情显示在页面

    前言:项目中用到了emoji表情,后端传递数据时直接是以Unicode形式,在页面总是无法展示,找尽各种方法总算是试出了一种,虽然达到效果但是并不是特别理解其中的原理并且无比笨拙,贴在这用作笔记,如果 ...

  3. IOS Emoji表情

    IOS Emoji 前言:我比较喜欢有趣的东西,有一些有趣的小东西,可能不是多么多么牛逼,也可能不需要多高深的技巧,也不会为其他什么强大的功能而服务,但是有时候将很多有趣的小东西组合起来运用,比如在你 ...

  4. 有关emoji表情以及utf-16编码

        昨日IOS组的同事遇到一个棘手的问题:当输入框内含有emoji表情时,如何获取文本框内的字符数(一个emoji表情算一个字符).       先从我最近接触的JAVA说起,JAVA中,在使用S ...

  5. iOS emoji表情转码 或者判断

    如果项目中有评论或者信息恢复的地方,往往会用到emoji,有时候如后台不支持emoji,就会显示乱码错误,我们可以把emoji转成unicode编码或者utf8编码格式传给服务器.当然如果后台服务器接 ...

  6. mysql支持IOS的Emoji表情

    原因: UTF-8编码有可能是两个.三个.四个字节.Emoji表情是4个字节,而Mysql的utf8编码最多3个字节,所以数据插不进去. 解决办法: 将Mysql的编码从utf8转换成utf8mb4 ...

  7. iOS 获取emoji表情和拦截emoji表情

      1 2 //将数字转为 #define EMOJI_CODE_TO_SYMBOL(x) ((((0x808080F0 | (x & 0x3F000) >> 4) | (x &a ...

  8. 让mysql支持emoji表情

    一.问题及原因 APP产品想对Emoji进行支持,但发现mysql数据库无法写入表情.原因是我们的mysql数据库默认用的是utf8编码,utf8编码存储时用的是三个字节,但Emoji表情是4个字节, ...

  9. emoji表情初探

    2015年12月28日 14:24:51 星期一 首先注意的地方: 1. emoji是需要操作系统支持的, 例如: ios更新时, 会在升级日志里说明, 增加了对多少个emoji图标的支持. 原理上是 ...

随机推荐

  1. Vue监听键盘回车事件

    在写页面时遇见了登录页需要加一个键盘回车事件. vue 的 v-on中有这样的修饰符 <input v-on:keyup.enter="submit"> 即<in ...

  2. Java学习多线程第一天

    内容介绍 Thread 线程创建 线程池 线程状态图 1 多线程 1.1     多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念. 进程:进程指正在运行的程序.确切的来说,当一个程序 ...

  3. java中什么是继承笔记

    继承 怎样实现继承:1,先提取共有的属性和方法,放到一个类里,这个叫父类.基类.超类        2.编写子类 修饰符 class 子类名 extends 父类名 好处:提高代码的复用性 子类怎么去 ...

  4. PDF.js 详情解说

    pdf.js资源下载 点我下载 自定义默认加载的pdf资源 在web/view.js中我们可以通过DEFAULT_URL设置默认加载的pdf.通过上面代码我们也可以看出来可以通过后缀名来指定加载的pd ...

  5. git submodule 子模块

    ### 背景:为什么要用子模块? 在开发项目中可能会遇到这种问题:在你的项目中使用另一个项目,也许这是一个第三方开发的库,或者是你独立开发的并在多个父项目中使用的.简单来说就是A同学开发了一个模块,被 ...

  6. ZooKeeper系列(五)—— ACL 权限控制

    一.前言 为了避免存储在 Zookeeper 上的数据被其他程序或者人为误修改,Zookeeper 提供了 ACL(Access Control Lists) 进行权限控制.只有拥有对应权限的用户才可 ...

  7. 5.MySQL数据库操作步骤

    第一步:登录到MySQL服务器 第二步:选择当前要操作的数据库 第三步:设置请求和返回数据的字符集 第四步:执行SQL语句 l 增加记录:INSERT INTO news(title,content) ...

  8. Python模块之snmp-cmds,easysnmp

    一.简介 snmp-cmds模块通过SNMP与目标设备进行通信,此模块适用于windows,此模块是基于系统已安装了net-snmp环境easysnmp模块通过SNMP与谬表设备进行通信,此模块用于l ...

  9. [HAOI2015]树上染色(树上dp)

    [HAOI2015]树上染色 这种要算点对之间路径的长度和的题,难以统计每个点的贡献.这个时候一般考虑算每一条边贡献了哪些点对. 知道这个套路以后,那么这题就很好做了. 状态:设\(dp[u][i]\ ...

  10. 一篇文章让你马上入门Hibernate

    在前面我们学完了Struts2,接下来我们就要去学习第二个框架Hibernate. 那什么是Hibernate? Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对 ...