码迷,mamicode.com
首页 > 其他好文 > 详细

Emoji与unicode特殊字符的处理

时间:2015-05-22 18:46:39      阅读:288      评论:0      收藏:0      [点我收藏+]

标签:

最近遇到了一个很让人纠结的问题: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

 

Emoji与unicode特殊字符的处理

标签:

原文地址:http://www.cnblogs.com/hahahjx/p/4522913.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!