欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java emoji持久化mysql

程序员文章站 2022-11-08 13:46:05
好久没有更新博客了,今天和大家分享一个关于emoji表情持久化问题,相信做web开发的都遇到过这样的问题,因为我们知道mysql的utf-8字符集保存不了保存不了表情字符,这是为什么呢?因为普通的字符串或者表情都是占位3个字节,所以utf8足够用了,但是移动端的表情符号占位是4个字节,普通的utf8 ......

  好久没有更新博客了,今天和大家分享一个关于emoji表情持久化问题,相信做web开发的都遇到过这样的问题,因为我们知道mysql的utf-8字符集保存不了保存不了表情字符,这是为什么呢?因为普通的字符串或者表情都是占位3个字节,所以utf8足够用了,但是移动端的表情符号占位是4个字节,普通的utf8就不够用了,为了应对无线互联网的机遇和挑战、避免 emoji 表情符号带来的问题、涉及无线相关的 mysql 数据库建议都提前采用 utf8mb4 字符集,这必须要作为移动互联网行业的一个技术选型的要点。

  好了看到上面的结果你是不是已经去修改数据库字符集了,如果你是个人项目或小项目上面的方法倒是一个解决方法,但是对于一个目前正在服务5000w用户的系统,上面的方式就有点不合适了,针对这种情况我这边总结了三种处理方式,下面分享给大家:

  1、既然是由于移动端的表情符号占位是4个字节,那我们直接把数据转换后保存。

1.urlencoder.encode(string s, string enc) 
使用指定的编码机制将字符串转换为 application/x-www-form-urlencoded 格式 

urldecoder.decode(string s, string enc) 
使用指定的编码机制对 application/x-www-form-urlencoded 字符串解码。 

  2、方法一的处理太粗躁,有没有更好的解决办法呢?使用轻量级工具emoji-java处理emoji表情字符

github地址:https://github.com/vdurmont/emoji-java

  具体使用方式,大家可以进入git中自行查看。

  3、有了上面两种方式,你是不是已经满足了,最为自己最推崇的emoji处理方式,下面才是重点,首先说一下上面两种方式存在的问题:第一种方式,数据经过转换,相当于加密,我们将无法直接查看到数据的原始内容,由其对于需要进行搜索的业务场景,将是一件很困难的事情;第二种方式,虽然避免了第一种方式存在的问题,但是它基于表情的对照表进行匹配转换的,也就意味着对于一些新表情,无法做到转换,这就会导致我们数据插入继续出现问题,这是它第一个问题,第二点在于它将表情转化为对应的匹配规则,说白一点就是转化为英文描述,就是这个转化,原本4个字节的表情,它可能给你转成了10个字节甚至更多。好了说了这么多下面我们看一下我最后的终极解决方法:

/**
 * @author: gaoshang
 * @description:
 * @date: 2019/7/19
 */
public class emojiutil {

    /**
     * 将文本中的表情转为十六进制
     * <p>
     *
     * @param input
     * @return
     */
    public static string parsefromaliases(string input) {
        if (input == null) {
            return input;
        }

        return stringtounicode(input);
    }

    /**
     * 将文本中的十六进制转为表情
     * <p>
     *
     * @param input
     * @return
     */
    public static string parsetoaliases(string input) {
        if (input == null) {
            return input;
        }

        return unicodetostring(input);
    }

    /**
     * 字符串转unicode
     *
     * @param str
     * @return
     */
    public static string stringtounicode(string str) {
        stringbuilder sb = new stringbuilder();
        stringbuilder cachesb = new stringbuilder();
        char[] c = str.tochararray();
        for (int i = 0; i < c.length; i++) {
            if (!isemojicharacter(c[i])) {
                if (cachesb.length() > 0) {
                    sb.append("\\u").append(cachesb);
                    cachesb.delete(0, cachesb.length());
                }

                sb.append("\\u").append("[").append(integer.tohexstring(c[i])).append("]");
            } else {
                if (c[i] == '[' || c[i] == '\\' || c[i] == ']') {
                    if (cachesb.length() > 0) {
                        sb.append("\\u").append(cachesb);
                        cachesb.delete(0, cachesb.length());
                    }
                    sb.append("\\u").append(c[i]);
                } else {
                    cachesb.append(c[i]);
                }
            }
        }
        if (cachesb.length() > 0) {
            if (sb.length() > 0) {
                sb.append("\\u");
            }
            sb.append(cachesb);
        }
        return sb.tostring();
    }

    /**
     * unicode转字符串
     *
     * @param unicode
     * @return
     */
    public static string unicodetostring(string unicode) {
        stringbuilder sb = new stringbuilder();
        string[] hex = unicode.split("\\\\u");
        for (int i = 0; i < hex.length; i++) {
            if (hex[i].indexof("[") == 0 && hex[i].indexof("]") == hex[i].length() - 1) {
                try {
                    int index = integer.parseint(hex[i].substring(1, hex[i].length() - 1), 16);
                    sb.append((char) index);
                } catch (numberformatexception e) {
                    sb.append(hex[i]);
                }
            } else {
                sb.append(hex[i]);
            }
        }
        return sb.tostring();
    }

    private static boolean isemojicharacter(char codepoint) {
        return (codepoint == 0x0) || (codepoint == 0x9) || (codepoint == 0xa)
                || (codepoint == 0xd)
                || ((codepoint >= 0x20) && (codepoint <= 0xd7ff))
                || ((codepoint >= 0xe000) && (codepoint <= 0xfffd))
                || ((codepoint >= 0x10000) && (codepoint <= 0x10ffff));
    }

}

  好了就先这样,欢迎大家提出不同的看法,已经好的解决方案。