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

Java处理emoji

程序员文章站 2022-03-24 11:49:04
1.问题产生情况 我遇到这个问题是做微信开发的时候有些有用的头像用了微信的emoji表情,然而我的mysql数据库用的编码是utf8_general_ci,就是utf-8编码,结果也就报错误了。 2.为什么会出现这种原因 因为mysql的utf8编码的一个字符最多3个字节,但是一个emoji表情为4 ......

1.问题产生情况

我遇到这个问题是做微信开发的时候有些有用的头像用了微信的emoji表情,然而我的mysql数据库用的编码是utf8_general_ci,就是utf-8编码,结果也就报错误了。

 

2.为什么会出现这种原因

因为mysql的utf8编码的一个字符最多3个字节,但是一个emoji表情为4个字节,所以utf8不支持存储emoji表情。但是utf8的超集utf8mb4一个字符最多能有4字节,所以能支持emoji表情的存储。

 

3.解决方法之一

把你的数据库编码集设置为utf8mb4,无论是数据库还是表,还是字段。虽然会增加存储,但是这个可以忽略不计。

 

4.解决方法之二

有句话说得好,问题来了要么解决要么折中解决。如果有些原因你不能修改数据库编码之类的,你可以用java的一些插件,如emoji-java这种emoji表情插件对表情进行特殊处理,然后保存或者去掉表情,这也是一种解决方法哦。

 

最后来段代码

package com.mojxtang;

import org.apache.commons.lang3.stringutils;

/**
 * <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)
 * 
 * 参考:
 * 百度百科: https://baike.baidu.com/item/emoji/8154456?fr=aladdin
*emoji表情:http://www.fhdq.net/emoji/emojifuhao.html
 * 杂项象形符号: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>
 */
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 (stringutils.isblank(src)) {
			return src;
		}
		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; index++) {
			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);
				// hash 长度,4位1个字节
				index += (hash.length() - 1) / 4;
			} else {
				sb.append((char) codepoint);
			}
		}
		return sb.tostring();
	}

	/** 解析可见字符标识字符串 */
	public static string reverse(string src) {
		// 查找对应编码的标识位
		if (stringutils.isblank(src)) {
			return src;
		}
		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();
	}
}

  博客地址:java处理emoji