好久沒有更新博客了,今天和大家分享一個關於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)); } }
好了就先這樣,歡迎大家提出不同的看法,已經好的解決方案。