Emoji与unicode特殊字符的处理
遇到了一个很让人纠结的问题:emoji表情在使用的过程中,会莫名其妙的消失,或者变成乱码,同时数据库用utf8mb4来存储,但是也出现了问题,冷备过后,导入进库的时候,变成了不可见字符,神奇的消失了!查阅了网上的解决办法,没有找到相应的解决方案,于是决定自己研究unicode,并且处理,发现了几个主要知识点:unicode被逻辑分为了17个Plane,每个Plane存65536个代码点。而java的 char最多只有2字节(16 bit),也就是说,他最多只能存储65536个字符,而那么问题来了,大于0x10000的这些字符怎么处理? 很好这个办法,java也用了一个比较委婉的办法来解决,那么就是Character.codePoint()用int来存储。直接看代码吧,代码中有注释解释:
package etna.core.util; import org.eclipse.jetty.util.StringUtil;
import com.google.common.base.Strings;
import com.google.common.hash.Hashing; /**
* <pre>
* 本类的主要功能是将带有emoji的字符串,格式化成unicode字符串,并且提供可见unicode字符反解成emoji字符
*
*
* 相关识知点:
* <b>
* Unicode平面,
* BMP的字符可以使用charAt(index)来处理,计数可以使用length()
* 其它平面字符,需要用codePointAt(index),计数可以使用codePointCount(0,str.lenght())</b>
*
* Unicode可以逻辑分为17平面(Plane),每个平面拥有65536( = 216)个代码点,虽然目前只有少数平面被使
* 用。
* 平面0 (0000–FFFF): 基本多文种平面(Basic Multilingual Plane, BMP).
* 平面1 (10000–1FFFF): 多文种补充平面(Supplementary Multilingual Plane, SMP).
* 平面2 (20000–2FFFF): 表意文字补充平面(Supplementary Ideographic Plane, SIP).
* 平面3 (30000–3FFFF): 表意文字第三平面(Tertiary Ideographic Plane, TIP).
* 平面4 to 13 (40000–DFFFF)尚未使用
* 平面14 (E0000–EFFFF): 特别用途补充平面(Supplementary Special-purpose Plane, SSP)
* 平面15 (F0000–FFFFF)保留作为私人使用区(Private Use Area, PUA)
* 平面16 (100000–10FFFF),保留作为私人使用区(Private Use Area, PUA)
*
* 参考:
* 维基百科: http://en.wikipedia.org/wiki/Emoji
* GITHUB: http://punchdrunker.github.io/iOSEmoji/
* 杂项象形符号:1F300-1F5FF
* 表情符号:1F600-1F64F
* 交通和地图符号:1F680-1F6FF
* 杂项符号:2600-26FF
* 符号字体:2700-27BF
* 国旗:1F100-1F1FF
* 箭头:2B00-2BFF 2900-297F
* 各种技术符号:2300-23FF
* 字母符号: 2100–214F
* 中文符号: 303D 3200–32FF 2049 203C
* Private Use Area:E000-F8FF;
* High Surrogates D800..DB7F;
* High Private Use Surrogates DB80..DBFF
* Low Surrogates DC00..DFFF D800-DFFF E000-F8FF
* 标点符号:2000-200F 2028-202F 205F 2065-206F
* 变异选择器:IOS独有 FE00-FE0F
* </pre>
*
* @author Daniel.Zhan
* @version 1.0
* @date 2015年5月20日
*/
public class EmojiCharacterUtil { // 转义时标识
private static final char unicode_separator = '&';
private static final char unicode_prefix = 'u';
private static final char separator = ':'; private static boolean isEmojiCharacter(int codePoint) {
return (codePoint >= 0x2600 && codePoint <= 0x27BF) // 杂项符号与符号字体
|| codePoint == 0x303D
|| codePoint == 0x2049
|| codePoint == 0x203C
|| (codePoint >= 0x2000 && codePoint <= 0x200F)//
|| (codePoint >= 0x2028 && codePoint <= 0x202F)//
|| codePoint == 0x205F //
|| (codePoint >= 0x2065 && codePoint <= 0x206F)//
/* 标点符号占用区域 */
|| (codePoint >= 0x2100 && codePoint <= 0x214F)// 字母符号
|| (codePoint >= 0x2300 && codePoint <= 0x23FF)// 各种技术符号
|| (codePoint >= 0x2B00 && codePoint <= 0x2BFF)// 箭头A
|| (codePoint >= 0x2900 && codePoint <= 0x297F)// 箭头B
|| (codePoint >= 0x3200 && codePoint <= 0x32FF)// 中文符号
|| (codePoint >= 0xD800 && codePoint <= 0xDFFF)// 高低位替代符保留区域
|| (codePoint >= 0xE000 && codePoint <= 0xF8FF)// 私有保留区域
|| (codePoint >= 0xFE00 && codePoint <= 0xFE0F)// 变异选择器
|| codePoint >= 0x10000; // Plane在第二平面以上的,char都不可以存,全部都转
} /**
* 将带有emoji字符的字符串转换成可见字符标识
*/
public static String escape(String src) {
if (src == null) {
return null;
}
int cpCount = src.codePointCount(0, src.length());
int firCodeIndex = src.offsetByCodePoints(0, 0);
int lstCodeIndex = src.offsetByCodePoints(0, cpCount - 1);
StringBuilder sb = new StringBuilder(src.length());
for (int index = firCodeIndex; index <= lstCodeIndex;) {
int codepoint = src.codePointAt(index);
if (isEmojiCharacter(codepoint)) {
String hash = Integer.toHexString(codepoint);
sb.append(unicode_separator).append(hash.length()).append(unicode_prefix).append(separator).append(hash);
} else {
sb.append((char) codepoint);
}
}
return sb.toString();
} /** 解析可见字符标识字符串 */
public static String reverse(String src) {
// 查找对应编码的标识位
if (src == null) {
return null;
}
StringBuilder sb = new StringBuilder(src.length());
char[] sourceChar = src.toCharArray();
int index = 0;
while (index < sourceChar.length) {
if (sourceChar[index] == unicode_separator) {
if (index + 6 >= sourceChar.length) {
sb.append(sourceChar[index]);
index++;
continue;
}
// 自已的格式,与通用unicode格式不能互转
if (sourceChar[index + 1] >= '4' && sourceChar[index + 1] <= '6' && sourceChar[index + 2] == unicode_prefix && sourceChar[index + 3] == separator) {
int length = Integer.parseInt(String.valueOf(sourceChar[index + 1]));
char[] hexchars = new char[length]; // 创建一个4至六位的数组,来存储uncode码的HEX值
for (int j = 0; j < length; j++) {
char ch = sourceChar[index + 4 + j];// 4位识别码
if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f')) {
hexchars[j] = ch; } else { // 字符范围不对
sb.append(sourceChar[index]);
index++;
break;
}
}
sb.append(Character.toChars(Integer.parseInt(new String(hexchars), 16)));
index += (4 + length);// 4位前缀+4-6位字符码
} else if (sourceChar[index + 1] == unicode_prefix) { // 通用字符的反转
// 因为第二平面之上的,已经采用了我们自己转码格式,所以这里是固定的长度4
char[] hexchars = new char[4];
for (int j = 0; j < 4; j++) {
char ch = sourceChar[index + 2 + j]; // 两位识别码要去掉
if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f')) {
hexchars[j] = ch; // 4位识别码
} else { // 字符范围不对
sb.append(sourceChar[index]);
index++;
break;
}
sb.append(Character.toChars(Integer.parseInt(String.valueOf(hexchars), 16)));
index += (2 + 4);// 2位前缀+4位字符码
}
} else {
sb.append(sourceChar[index]);
index++;
continue;
}
} else {
sb.append(sourceChar[index]);
index++;
continue;
}
} return sb.toString();
} public static String filter(String src) {
if (src == null) {
return null;
}
int cpCount = src.codePointCount(0, src.length());
int firCodeIndex = src.offsetByCodePoints(0, 0);
int lstCodeIndex = src.offsetByCodePoints(0, cpCount - 1);
StringBuilder sb = new StringBuilder(src.length());
for (int index = firCodeIndex; index <= lstCodeIndex;) {
int codepoint = src.codePointAt(index);
if (!isEmojiCharacter(codepoint)) {
System.err.println("codepoint:" + Integer.toHexString(codepoint));
sb.append((char) codepoint);
}
index += ((Character.isSupplementaryCodePoint(codepoint)) ? 2 : 1); }
return sb.toString();
}
}
Emoji与unicode特殊字符的处理的更多相关文章
- emoji表情等特殊字符处理和存储的两个方案
方案1.改数据库配置 使之支持emoji表情等特殊字符,小公司或者个人开发还好,大公司用此方案代价较大. 以mysql为例,改配置方法参考:https://blog.csdn.net/u0107373 ...
- 由一个emoji引发的思考
由一个emoji引发的思考 从毕业以来,基本就一直在做移动端,但是一直就关于移动端的开发,各种适配问题的解决,在日常搬砖中处理了就过了,也没有把东西都沉淀下来,觉得甚是寒颜.现就一个小bug,让我们来 ...
- iOS 获取emoji表情和拦截emoji表情
1 2 //将数字转为 #define EMOJI_CODE_TO_SYMBOL(x) ((((0x808080F0 | (x & 0x3F000) >> 4) | (x &a ...
- emoji探寻之路
emoji是什么? http://www.baike.com/wiki/emoji emoji表情符号,是20世纪90年代由NTT Docomo栗田穣崇(Shigetaka Kurit)创建的,词义来 ...
- Emoji表情符号兼容方案(适用ios,android,wp等平台)
http://blog.csdn.net/qdkfriend/article/details/7576524 Emoji表情符号兼容方案 一 什么是Emoji emoji就是表情符号:词义来自日语(え ...
- UITextView/UITextField检测并过滤Emoji表情符号
UITextView/UITextField检测并过滤Emoji表情符号 本人在开发过程中遇到过这种情况,服务器端不支持Emoji表情,因此要求客户端在上传用户输入时,不能包含Emoji表情.在客户端 ...
- Emoji表情在网页中显示
最近遇到一个项目,客户手机上发送的表情要在电脑网页中显示,没有找到简便方法,于是有了以下方案. 由于Emoji表情传到后台是“口”,怎么找出接收数据中的表情是关键,各种搜索后,我用下面的正则表达式匹配 ...
- Love Hotels and Unicode[转]
原文地址:http://www.reigndesign.com/blog/love-hotels-and-unicode/ 讲得挺通俗的一篇文章 On Sunday 28 October I atte ...
- XMPP 服务器 Openfire 的 Emoji 支持问题(进行部分修改)
当前最新版3.9.3已经可以支持Emoji ----------------------------------------------------------------------------- ...
随机推荐
- Linux搜索查找类
find find将从指定目录下递归地遍历各个子目录,将满足条件的文件或目录显示在终端,如果没有则不显示 基本语法 find 搜索范围 [选项] 选项说明 选项 功能 -name 按照文件名查找 -u ...
- 【jvm】04-我偷偷改了你编译后的class文件
[jvm]04-我偷偷改了你编译后的class文件 欢迎关注b站账号/公众号[六边形战士夏宁],一个要把各项指标拉满的男人.该文章已在github目录收录. 屏幕前的大帅比和大漂亮如果有帮助到你的话请 ...
- 【优雅代码】02-java传个方法你会吗,不是Method对象
[优雅代码]02-java传个方法你会吗,不是Method对象 欢迎关注b站账号/公众号[六边形战士夏宁],一个要把各项指标拉满的男人.该文章已在github目录收录. 屏幕前的大帅比和大漂亮如果有帮 ...
- 使用 JavaScript 中的变量、数据类型和运算符,计算出两个 number 类型的变量与一个 string 类型的变量的和,根据 string 类型处于运算符的不同位置得到不同的结果
查看本章节 查看作业目录 需求说明: 使用 JavaScript 中的变量.数据类型和运算符,计算出两个 number 类型的变量与一个 string 类型的变量的和,根据 string 类型处于运算 ...
- 编写Java程序,随机给定一个数字猜大小
返回本章节 返回作业目录 需求说明: 由系统随机生成一个1~100之间的整数. 通过控制台一直输入一个整数,比较该数与系统随机生成的那个数,如果大就输出"猜大了.",继续输入:如果 ...
- 基于GO语言实现的支持高并发订单号生成函数
1.固定24位长度订单号,毫秒+进程id+序号. 2.同一毫秒内只要不超过一万次并发,则订单号不会重复. github地址:https://github.com/w3liu/go-common/blo ...
- Selenium_获取浏览器名称和版本(5)
from selenium import webdriver driver = webdriver.Chrome() driver.maximize_window() driver.get(" ...
- 华为云 Kubernetes 管理员实训 四 课后作业
练习一 创建一个Service和一个Pod作为其后端.通过kubectl describe获得该Service和对应Endpoints的信息. Service的名称为<hwcka-004-1-s ...
- linux如何查看服务器当前的并发访问量
linux如何查看服务器当前的并发访问量 [root@localhost ~]# netstat -pnt | grep :80 | wc -l 2 [root@localhost ~]# netst ...
- java 使用 ArrayList 排序【包括数字和字符串】
1.数字排序 /** * 数字排序 */ @Test public void t2() { List<Integer> list = new ArrayList<>(); li ...