文档章节

不要小看小小的 emoji 表情

crossoverJie
 crossoverJie
发布于 09/10 08:22
字数 1924
阅读 4896
收藏 49

前言

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

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

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

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

<!--more-->

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


我首先尝试将一个 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 &#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

你的点赞与分享是对我最大的支持

© 著作权归作者所有

crossoverJie

crossoverJie

粉丝 731
博文 99
码字总数 191646
作品 0
江北
后端工程师
私信 提问
加载中

评论(25)

ma83632349
ma83632349
给自己挖坑编码解码很容易出错,况且是持久化数据,更改数据库编码才正确
f
freezingsky
如果是已有项目, 没办法 才做映射. 不然, 建议, 统一升级utf8mb4
oscyj
oscyj
这感觉是给自己挖坑了,升级数据库才是正道,一劳永逸
柿子白头
柿子白头
你的表情为什么这个样子,我的都糊了,根本看不清
mental
mental
emoji最大的问题是编码没标准的规范, 还得去查询码表, 新增了emoji还得更新码表, 难做向上兼容. 另外文中说UTF-16、UTF-32用的不多, UTF8更常用,其实 utf8更多用在传输,储存, 而程序处理更多是utf16,如js , ios , android, 方便内存线性访问
天命剑主
天命剑主
utf-8就能存吧;
MGL_TECH
MGL_TECH
不行,升级数据库吧😆
天命剑主
天命剑主
utf-8可变长度,支持到4字节没问题的
MGL_TECH
MGL_TECH
还是升级保险
Tobyee
Tobyee
所以说,国内的大学计算机专业课程的设计和教学体验真是一团糟,学生知其然不知其所以然,出来工作后也就一直在应用层打滚,基建层才缺乏人才。
云淡V
云淡V
主要基层职位少,工资不高呀
墨盒
墨盒
图里不是两个char吗?正好四个字节啊,怎么就得出是utf16编码的?
dingdayu
dingdayu
文章不错,但是大哥应该没做过微信公众平台😬
廖萍
微信公众号会有什么不一样吗?
dingdayu
dingdayu
因为里面用户昵称,首先要处理的就是这个问题😁
廖萍
原来是这样
J
Jason909
我用navicat11显示mysql里面的emoji为方块,升级到12显示正常,dbeaver也显示正常。
过滤字符串中的Emoji表情[转]

iOS 5.0之前,苹果都是采用3个字节来承接 emoji 表情,Java 的普通 char 可以支持显示。但 iOS 5.0 之后, 苹果升级了系统自带的 emoji 表情输入法,用的 Unicode 6 标准来统一,是采用4个 ...

彭博
2013/04/08
41.5K
8
微信nickname乱码(emoji)及mysql编码格式设置(utf8mb4)解决的过程

网上的解决办法大多是修改my.cnf参数,设置mysql的编码为utf8mb4,这种方法虽然彻底,但是通常要重启mysql,会造成生产系统临时当机。我认为写的比较好的方法是:mysql/Java服务端对emoji的支...

Jack088
05/16
130
0
分享6个优雅的程序员消遣的创意涂鸦趣站

收集了一些非常有意思的创意涂鸦趣站 ,今天与大伙儿分享分享~~炫酷的趣站助你缓解身心,消遣时光 ·发挥你无穷的创造力前来涂鸦吧!一秒让你绘制的作品变为高大上的桌面壁纸~嘻嘻嘻 快来试...

Janet_zyh
2017/11/20
0
0
没有 emoji 表情,你是不是就不会打字了?

11 月 16 日,牛津辞典宣布了 2015 年年度单词。但是,这并不是一个严格意义上的单词,因为它是一个 Emoji 表情,这个表情意味着「喜极而泣」(Face with Tears of Joy)。 再往前推一个月,...

oschina
2015/12/13
3.7K
27
Unicode 11候选版发布,新增“OK”笑脸等67个Emoji表情

由于 Unicode 11 尚未正式发布,届时这些表情设计可能还会有变动。 就在今年6月底,统一码技术委员会(Unicode Consortium)正式公布了Unicode 10当中可供用户使用的56个新Emoji表情。据Emo...

局长
2017/08/05
1K
8

没有更多内容

加载失败,请刷新页面

加载更多

什么是线程死锁,如何解决

产生死锁的条件有四个: 互斥条件:所谓互斥就是进程在某一时间内独占资源。 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:进程已获得资源,在末使用完...

苏坡吴
43分钟前
5
0
CSS

一、CSS概述 1、什么是CSS Cascading style sheets,层叠样式表、级联样式表,简称样式表 2、css的作用 设置HTML网页中元素的样式 3、HTML与CSS的关系 HTML:负责网页的搭建,内容展示--网页...

wytao1995
54分钟前
6
0
二叉查找树的第 K 个结点

private TreeNode ret;private int cnt = 0;public TreeNode KthNode(TreeNode pRoot, int k) { inOrder(pRoot, k); return ret;}private void inOrder(TreeNode root......

Garphy
今天
4
0
windo8 weblogic

需要的软件包 现在安装jdk 则先进入你电脑自带jdk \bin目录下 然后java -jar 执行你的jar包就可以了 欢迎界面直接点击下一步,跳到更新界面,直接选择跳过 然后选择安装目录(注意:目录不要有...

恩多
今天
8
0
Activiti 批注

Activiti添加批注(comment)信息 在每次提交任务的时候需要描述一些批注信息,例如:请假流程提交的时候要描述信息为什么请假,如果领导驳回可以批注驳回原因等  1、添加批注 // 由于流程...

奔跑的android
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部