过滤Emoji表情

引子:

  最近有个类似购物商城的项目,需要添加商品备注字段(字符串)。然后测试提了一个bug ,emoji表情保存直接报错...   产品立刻找到我说,就算保存不了也不能报错啊。我debug一看,emoji 在前端依旧显示的是表情,但是在后端是乱码,mysql保存报错也是正常的。其实我也不知道到底是错在哪里,我觉得前端应该传字符串给我才对。但是前端说前端是弱语言类型,没有办法判断传参是不是字符串。我..... 于是我就开始琢磨这个玩意到底是个啥?为啥会报错?咋个解决?

部分内容转自 https://www.cnblogs.com/eric-fang/p/4939058.html ,https://www.jianshu.com/p/64ec0f6b6245 等等

  百度了很久,才搞清楚是什么鬼。首先报错是因为字符格式不支持?嗯?数据库不支持?支持不就完了?公用的数据库的存储编码肯定不是我一个小开发说了算的。关于这些字符编码的基础知识都忘的差不多了。又重新回顾了一下。

  

  字符编码:

  简单的理解,计算机只认数字,为了表示文字,我们制定了一个字符到数字的映射。 这个映射就叫做编码 。 最出名的就是ASCII码,它将英文字母的大小写,数字0-9,以及一些标点符号和控制字符映射为0-127 这些整型.  正常一个字节的表示范围是-128到127,所以一个八位(字节) 就足以保存了。但是语言种类很多,26个英文字母远远不够,于是人们就开发出来了Unicode编码,最初,Unicode 编码是被设计为 16 位的,提供了 65,536 个字符的空间。当时人们认为这已经大到足够编码世界上现代文本里所有的文字和字符了。后来,考虑到要编码历史上的文字以及一些很少使用的日本汉字和中国汉字,Unicode 编码扩展到了 21 位(从 U+0000 到 U+10FFFF)。

  Unicode 的基本元素,它的“字符”,应该叫做编码点(code point)。通过数字来区分,通常写成16进制的形式再加前缀“U+”,比如U+0041 表示字母 “A”。但是,如果所有存储都用这种存储方式,必然会造成大量的空间浪费。于是日常够用的编码 UTF-8 应运而生,成为主流。并且UTF-8兼容ASCII码,无需转换。在UTF-8编码中,一个字符占用两个字节16位。

  java中的英文字母和汉字都是使用Unicode 编码来表示的,utf-8是国际标准,一个汉字(中文字符)使用3个字节来表示,gbk 是中国国标,中文只存储两个字符,不是国际标准。

  搞清楚字符编码的问题了就能确定思路了。Emoji 是个后加的全新的码点。把字符串转回unicode,对unicode的每个码点校验,只要不是在常用的码点(字符)范围内均替换为□ 表示特殊字符,不支持解析,嘿嘿嘿。至此,问题解决,上代码。



/** 
   * 过滤emoji 或者 其他非文字类型的字符 
   * @param source 
   * @return 
   */
   public static String filterEmoji(String source) {
     int len = source.length();             // 返回unicode代码单元(码点)的数量
     StringBuilder buf = new StringBuilder(len);  
     for (int i = 0; i < len; i++) {
       char codePoint = source.charAt(i);      //取到每个码点
       if (isNotEmojiCharacter(codePoint)) {    //判断是否是常用字符
         buf.append(codePoint);            //如果是,就正常添加
       } else {
         buf.append("□");                //不是说明可能是emoji或者其他不支持的特殊字符,使用 □ 替换
} } return buf.toString(); }

  

   private static boolean isNotEmojiCharacter(char codePoint) {        //判断是否为常用字符码点段
     return (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD)
         || ((codePoint >= 0x20) && (codePoint <= 0xD7FF))
         || ((codePoint >= 0xE000) && (codePoint <= 0xFFFD));
   }

  还看到有人直接使用源码来转换字符串和unicode . 了解编码位数后理解也不难,就是挺耗时间的, 上边的解法足以 解决需求。

  第一篇博客,算是给自己回顾回顾基础知识了。

posted @ 2019-04-12 17:10  爱跑步的c先森  阅读(778)  评论(0编辑  收藏  举报