标签:
最近遇到了一个很让人纠结的问题:emoji表情在使用的过程中,会莫名其妙的消失,或者变成乱码,同时数据库用utf8mb4来存储,但是也出现了问题,冷备过后,导入进库的时候,变成了不可见字符,神奇的消失了!查阅了网上的解决办法,没有找到相应的解决方案,于是决定自己研究unicode,并且处理,发现了几个主要知识点:unicode被逻辑分为了17个Plane,每个Plane存65536个代码点。而java的 char最多只有2字节(16 bit),也就是说,他最多只能存储65536个字符,而那么问题来了,大于0x10000的这些字符怎么处理? 很好这个办法,java也用了一个比较委婉的办法来解决,那么就是Character.codePoint()用int来存储。直接看代码吧,代码中有注释解释:
1 package etna.core.util; 2 3 4 /** 5 * <pre> 6 * 本类的主要功能是将带有emoji的字符串,格式化成unicode字符串,并且提供可见unicode字符反解成emoji字符 7 * 8 * 9 * 相关识知点: 10 * <b> 11 * Unicode平面, 12 * BMP的字符可以使用charAt(index)来处理,计数可以使用length() 13 * 其它平面字符,需要用codePointAt(index),计数可以使用codePointCount(0,str.lenght())</b> 14 * 15 * Unicode可以逻辑分为17平面(Plane),每个平面拥有65536( = 216)个代码点,虽然目前只有少数平面被使 16 * 用。 17 * 平面0 (0000–FFFF): 基本多文种平面(Basic Multilingual Plane, BMP). 18 * 平面1 (10000–1FFFF): 多文种补充平面(Supplementary Multilingual Plane, SMP). 19 * 平面2 (20000–2FFFF): 表意文字补充平面(Supplementary Ideographic Plane, SIP). 20 * 平面3 (30000–3FFFF): 表意文字第三平面(Tertiary Ideographic Plane, TIP). 21 * 平面4 to 13 (40000–DFFFF)尚未使用 22 * 平面14 (E0000–EFFFF): 特别用途补充平面(Supplementary Special-purpose Plane, SSP) 23 * 平面15 (F0000–FFFFF)保留作为私人使用区(Private Use Area, PUA) 24 * 平面16 (100000–10FFFF),保留作为私人使用区(Private Use Area, PUA) 25 * 26 * 参考: 27 * 维基百科: http://en.wikipedia.org/wiki/Emoji 28 * GITHUB: http://punchdrunker.github.io/iOSEmoji/ 29 * 杂项象形符号:1F300-1F5FF 30 * 表情符号:1F600-1F64F 31 * 交通和地图符号:1F680-1F6FF 32 * 杂项符号:2600-26FF 33 * 符号字体:2700-27BF 34 * 国旗:1F100-1F1FF 35 * 箭头:2B00-2BFF 2900-297F 36 * 各种技术符号:2300-23FF 37 * 字母符号: 2100–214F 38 * 中文符号: 303D 3200–32FF 2049 203C 39 * Private Use Area:E000-F8FF; 40 * </pre> 41 * 42 * @author Daniel.Zhan 43 * @version 1.0 44 * @date 2015年5月20日 45 */ 46 public class EmojiCharacterUtil { 47 48 // 转义时标识 49 private static final char unicode_separator = ‘\\‘; 50 private static final char unicode_prefix = ‘u‘; 51 private static final char separator = ‘:‘; 52 53 private static boolean isEmojiCharacter(int codePoint) { 54 return (codePoint >= 0x2600 && codePoint <= 0x27BF) // 杂项符号与符号字体 55 || codePoint == 0x303D 56 || codePoint == 0x2049 57 || codePoint == 0x203C 58 || (codePoint >= 0x3200 && codePoint <= 0x32FF)// 中文符号 59 || (codePoint >= 0x2100 && codePoint <= 0x214F)// 字母符号 60 || (codePoint >= 0x2B00 && codePoint <= 0x23FF)// 箭头 各种技术符号 61 || (codePoint >= 0x2900 && codePoint <= 0x297F)// 箭头B 62 || codePoint >= 0x10000; // Plane在第二平面以上的,char都不可以存,全部都转 63 } 64 65 /** 66 * 将带有emoji字符的字符串转换成可见字符标识 67 */ 68 public static String escape(String src) { 69 if (src == null) { 70 return null; 71 } 72 StringBuilder sb = new StringBuilder(src.length()); 73 for (int i = 0; i < src.codePointCount(0, src.length()); i++) { 74 int codepoint = src.codePointAt(i); // 避免字符处于第二区间或者之上,丢失高位字节 75 if (isEmojiCharacter(codepoint)) { 76 String hash = Integer.toHexString(codepoint); 77 sb.append(unicode_separator).append(hash.length()).append(unicode_prefix).append(separator).append(hash); 78 } else { 79 sb.append((char) codepoint); 80 } 81 } 82 return sb.toString(); 83 } 84 85 /** 解析可见字符标识字符串 */ 86 public static String reverse(String src) { 87 // 查找对应编码的标识位 88 if (src == null) { 89 return null; 90 } 91 StringBuilder sb = new StringBuilder(src.length()); 92 char[] sourceChar = src.toCharArray(); 93 int index = 0; 94 while (index < sourceChar.length) { 95 if (sourceChar[index] == unicode_separator) { 96 if (index + 6 >= sourceChar.length) { 97 sb.append(sourceChar[index]); 98 index++; 99 continue; 100 } 101 // 自已的格式,与通用unicode格式不能互转 102 if (sourceChar[index + 1] >= ‘4‘ && sourceChar[index + 1] <= ‘6‘ && sourceChar[index + 2] == unicode_prefix && sourceChar[index + 3] == separator) { 103 int length = Integer.parseInt(String.valueOf(sourceChar[index + 1])); 104 char[] hexchars = new char[length]; // 创建一个4至六位的数组,来存储uncode码的HEX值 105 for (int j = 0; j < length; j++) { 106 char ch = sourceChar[index + 4 + j];// 4位识别码 107 if ((ch >= ‘0‘ && ch <= ‘9‘) || (ch >= ‘a‘ && ch <= ‘f‘)) { 108 hexchars[j] = ch; 109 110 } else { // 字符范围不对 111 sb.append(sourceChar[index]); 112 index++; 113 break; 114 } 115 } 116 sb.append(Character.toChars(Integer.parseInt(new String(hexchars), 16))); 117 index += (4 + length);// 4位前缀+4-6位字符码 118 } else if (sourceChar[index + 1] == unicode_prefix) { // 通用字符的反转 119 // 因为第二平面之上的,已经采用了我们自己转码格式,所以这里是固定的长度4 120 char[] hexchars = new char[4]; 121 for (int j = 0; j < 4; j++) { 122 char ch = sourceChar[index + 2 + j]; // 两位识别码要去掉 123 if ((ch >= ‘0‘ && ch <= ‘9‘) || (ch >= ‘a‘ && ch <= ‘f‘)) { 124 hexchars[j] = ch; // 4位识别码 125 } else { // 字符范围不对 126 sb.append(sourceChar[index]); 127 index++; 128 break; 129 } 130 sb.append(Character.toChars(Integer.parseInt(String.valueOf(hexchars), 16))); 131 index += (2 + 4);// 1位前缀+4位字符码 132 } 133 } else { 134 sb.append(sourceChar[index]); 135 index++; 136 continue; 137 } 138 } else { 139 sb.append(sourceChar[index]); 140 index++; 141 continue; 142 } 143 } 144 145 return sb.toString(); 146 } 147 148 public static String filter(String src) { 149 if (src == null) { 150 return null; 151 } 152 StringBuilder sb = new StringBuilder(src.length()); 153 for (int i = 0; i < src.codePointCount(0, src.length()); i++) { 154 int codepoint = src.codePointAt(i); // 避免字符处于第二区间或者之上,丢失高位字节 155 if (!isEmojiCharacter(codepoint)) { 156 sb.append((char) codepoint); 157 } 158 } 159 return sb.toString(); 160 } 161 162 163 } 164 v
标签:
原文地址:http://www.cnblogs.com/hahahjx/p/4522913.html