背景知识 

  • emoji表情符号,是20世纪90年代由NTT Docomo栗田穣崇(Shigetaka Kurit)创建的,词义来自日语(えもじ,e-moji,moji在日语中的含义是字符)。emoji可以使数字通信做到让人如同面对面交流,避免错误传达信息。
  • 自苹果公司发布的iOS 5输入法中加入了emoji后,这种表情符号开始席卷全球,目前emoji已被大多数现代计算机系统所兼容的Unicode编码采纳,普遍应用于各种手机短信和社交网络中。
  • 所谓Emoji就是一种在Unicode位于\u1F601-\u1F64F区段的字符。这个显然超过了目前常用的UTF-8字符集的编码范围\u0000-\uFFFF

知识点

  • 在Java里UTF-8,只支持双字节即\u0000-\uFFFF,emoji(🐴) => "\uD83D\uDC34"
  • 查Symbola表,我们的目标对象大致是从
• 1F300-1F3FF => "\uD83C\uDF00" - "\uD83C\uDFFF"
• 1F400-1F4FF => "\uD83D\uDC00" - "\uD83D\uDCFF"
• 1F500-1F5FF => "\uD83D\uDD00" - "\uD83D\uDDFF"
• 1F600-1F6FF => "\uD83D\uDE00" - "\uD83D\uDEFF"
• 1F700-1F7FF => "\uD83D\uDF00" - "\uD83D\uDFFF"

编码知识

Code

UTF-8

UTF-16 LE

Surrogates

1F7FF

F0 9F 9F BF

3D D8 FF DF

D83D DFFF

UTF-16描述

Unicode的编码空间从U+0000到U+10FFFF,共有1,112,064个码位(code point)可用来映射字符. Unicode的编码空间可以划分为17个平面(plane),每个平面包含216(65,536)个码位。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0)。其他平面称为辅助平面(Supplementary Planes)。基本多语言平面内,从U+D800到U+DFFF之间的码位区段是永久保留不映射到Unicode字符。UTF-16就利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。 

UTF-16解码

lead \ trail

DC00

DC01

   …   

DFFF

D800

10000

10001


103FF

D801

10400

10401


107FF

  ⋮





DBFF

10FC00

10FC01


10FFFF

示例:

例如U+10437编码:

  • 0x10437减去0x10000,结果为0x00437,二进制为0000 0000 0100 0011 0111。
  • 分区它的上10位值和下10位值(使用二进制):0000000001 and 0000110111。
  • 添加0xD800到上值,以形成高位:0xD800 + 0x0001 = 0xD801。
  • 添加0xDC00到下值,以形成低位:0xDC00 + 0x0037 = 0xDC37。
  • 下表总结了该转换,以及其它。颜色指示如何从码点位被分布在所述的UTF-16字节。由UTF-16编码过程中加入附加位以黑色显示。

字符

普通二进制

UTF-16二进制

UTF-16 十六进制

字符代码

UTF-16BE

十六进制字节

UTF-16LE

十六进制字节

$

U+0024

0000 0000 0010 0100

0000 0000 0010 0100

0024

00 24

24 00


U+20AC

0010 0000 1010 1100

0010 0000 1010 1100

20AC

20 AC

AC 20

𐐷 

U+10437

0001 0000 0100 0011 0111

1101 1000 0000 0001 1101 1100 0011 0111

D801 DC37

D8 01 DC 37

01 D8 37 DC

𤭢

U+24B62

0010 0100 1011 0110 0010

1101 1000 0101 0010 1101 1111 0110 0010

D852 DF62

D8 52 DF 62

52 D8 62 DF

解决方案

一 数据库

  • jar包:mysql connector版本高于5.1.13
  • mysql:utf8mb4的最低mysql版本支持版本为5.5.3+
  • 从utf8改至utf8mb4,需要重启mysql
  • 由于RD不应更改mysql配置,所以需要在业务应用处,调用set names utf8mb4,以使数据以utf8mb4编码存储到数据库

二 过滤

    由于数据库的治本方法建立在有数据存储的所有涉猎系统都得满足上述条件,所以并不是常常满足。由此还需要一个治标的方法。


public static void main(String[] args) {
String source = "a\uD83D\uDE36b\uD83D\uDE36\uD83D\uDE36\uD83D\uDE36\uD83C\uDE3612312\uD83C\uDE36";
while (true) {
Integer pos = source.indexOf("\uD83D");
if (pos == -1) {
pos = source.indexOf("\uD83C");
}
if (pos != -1) {
source = source.substring(0, pos) + source.substring(pos + 2);
} else {
break;
}
}
System.out.println(source);
}