前言
之前也分享过很多工作中踩坑的经验:
今天再来分享工作中一个真实的案例:
商品评价列表页,显示每条用户的评价详情,为了保护用户隐私,要求显示用户昵称时只能显示第一位和最后一位,其他的用※代替。
例如输入:🐳🐳🐠,输出:🐳***🐠
看似一个平淡无奇的需求,我也没有太在意。服务端将用户的评论信息存储到db
中,评价列表接口就是将数据库中该商品的评论信息展示出来,特殊处理下评论人的昵称就可以了。
但是!! 测试同学发现用户昵称包含emoji表情
时就会出问题,切割的数据会有问号显示!!
模拟的示例代码如下:
输出:
看到这个输出,我真的是一脸懵逼,这完全不是我想要的结果呀!!!
这三个鱼可算是难倒我了,难道只能给测试说 emoji太特殊 不予处理?然后撒个娇蒙混过关?
思考了良久,我还是决定要正视这个问题并解决掉它!(毕竟我还是那个不畏困难的小机灵鬼🤔)
PS:本文很大程度是受到之前公司一位同事unicode分享的启发,在这里向我的这位老师致敬!下面的内容会一步步分析这个问题的产生以及最终的解决方案。
概念常识
要解决这些问题,就必须要铺垫一些基础知识,大家等不及看解决方案 可以拉到文章最后的代码示例。
utf8mb4
一般我们在数据库创建表时都会默认使用这种编码格式:
相信大家对这个编码格式都不陌生吧,当我们想存储emoji
数据到数据库中,那么数据库的格式就需要指定为utf8mb4
了,要不然存储就会报错了。所以在很多公司的db规范
中,数据库默认编码必须为utf8mb4
但是大家有没有过这样的疑惑,为何utf8
不行而utf8mb4
就行?这里面到底有什么弯弯道道?
这里面涉及到unicode
相关知识,我们下面会提到,大家继续看。
在mysql 5.5
之前,utf8
编码只支持1-3
个字节,从mysql 5.5
开始,可支持4个字节UTF
编码utf8mb4
,一个字符最多能有4字节,所以能支持更多的字符集。
这个表格中包含了所有的 emoji
以及它所对应的 unicode
编码,同时也有对应的 utf-8
编码的实现。
从图中也可以看出 emoji
表情用 utf-8
表示时会占用 4个字节,这也就是为什么数据库用utf8
无法存储emoji
表情的原因了。
同样我们也可以在java
代码中看看emoji
占用几个字节长度:
我们也可以看到String.getBytes()
,默认是utf-8
编码的:
ASCII码
上面介绍utf8mb4
时有提过unicode
,介绍它之前我们也需要先提一嘴我们的老朋友:ASCII
码
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语。
这样我们就可以使用一个字节来表示现代英文,看起来非常不错,部分数据对应关系如下:
但这个只能显示的代表拉丁文,这显然是远远不够的。
Unicode
显而易见,计算机的发展并不是只支持英文一种语言的,ASCII
的局限在于只能显示26个
基本拉丁字母、阿拉伯数字和英式标点符号,因此只能用于显示现代美国英语。
这时如果能有一种包含了世界上所有的文字的字符集,每一个地区的文字都在这个字符集中有唯一的二进制表示,这样便不会出现乱码问题了。所以Unicode
也应运而生了。
概念
Unicode,中文又称万国码、国际码、统一码、单一码,是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。
平面
Unicode
首先承认了 ASCII
占用 0-127 整数资源的合法性,之后又一次占用了 128-65535
的整数资源,有了这么多的整数资源,我们就可以把世界各种文字的每一种字符分配一个整数来表示了。
之后,Unicode
联盟发现 65536 个整数也不够分配的,于是就索性一次性又把之后的 16 个 65536 的数字即 65536-1114111 的整数资源给占了,然后把多占的 16 个 65536 的段分别命名为 16 个平面,加上原来的 0-65535 平面,Unicode
总共有 17 个平面