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();
	}
}

博客地址:http://blog.mojxtang.com

Java处理emoji的更多相关文章

  1. java过滤emoji表情

    import java.util.regex.Matcher; import java.util.regex.Pattern; public class test { /** * 表情过滤 * */ ...

  2. java过滤emoji表情(成功率高)

    转载自:http://blog.csdn.net/huangchao064/article/details/53283738 基本能过滤大部分的ios,安卓,微信emoji表情 有很多别的帖子搜出来很 ...

  3. Java转义emoji等特殊符号

    写在前面 网上找了很多转emoji等方法,大多有两种方法 更改数据库编码格式为utf8mb4 过滤字符串中的emoji 都不是很优雅 更改数据库编码,势必影响其他数据库 过滤emoj效率比较低 处理E ...

  4. Java 中 Emoji 的正则表达式

    一.emoji 的范围 查阅维基百科中 emoji 的说明 1. 杂项符号及图形 杂项符号及图形一共有768个字符,范围为: U+1F300 - U+1F5FF,在 Java 中正则表达式为: &qu ...

  5. java 处理emoji表情信息转换为String

    2种方式实现: 注意:如果发现运行时java.lang.NoClassDefFoundError:异常就是缺少了jar包.添加对应的jar包就可以. 一.emoji-java-4.0.0.jar实现 ...

  6. java处理emoji(转)

    最近对接ios.安卓客户端,需要处理emoji等表情符号,网上总结: 1.过滤掉emoji表情符 2.修改数据库的编码格式等,让其支持存储emoji 以下分别对两种方案进行描述: 第一种:过滤掉emo ...

  7. java转换emoji表情

    /** * @Description 将字符串中的emoji表情转换成可以在utf-8字符集数据库中保存的格式(表情占4个字节,需要utf8mb4字符集) * @param str * 待转换字符串 ...

  8. Java 解决Emoji表情过滤问题(转载)

    本文作者 我是周洲 原文链接 https://blog.csdn.net/u012904383/article/details/79376707 本人使用的是第三种引入jar的方法 问题: Emoji ...

  9. Java 解决Emoji表情过滤问题

    Emoji表情从三方数据中获取没有过滤,导致存入DB的时候报错. 原因: UTF-8编码有可能是两个.三个.四个字节.Emoji表情是4个字节,而Mysql的utf8编码最多3个字节,所以数据插不进去 ...

随机推荐

  1. 【OCP题库-12c】最新CUUG OCP 071考试题库(69题)

    69.(31-1)choose the best answer: Evaluate the following query: SELECT INTERVAL '300' MONTH, INTERVAL ...

  2. 【文文殿下】后缀自动机(SAM)求最长公共子串的方法

    首先,在A 串上建立一个SAM,然后用B串在上面跑.具体跑的方法是: 从根节点开始,建立一个指针 p ,指着B串的开头,同步移动指针,沿着SAM的边移动,如果可以移动(即存在边)那么万事皆好,直接le ...

  3. MySQL数据库密码破解

    研究MySQL数据库的加解密方式,在网络攻防过程中具有重要的意义:试想一旦获取了网站一定的权限后,如果能够获取MySQL中保存用户数据,通过解密后,即可通过正常途径来访问数据库:一方面可以直接操作数据 ...

  4. springboot 使用itextpdf 框架实现多个图片合成一个pdf文件

    以下两个方法引入头 import com.lowagie.text.*; import com.lowagie.text.pdf.PdfWriter; import org.apache.pdfbox ...

  5. 定期删除Azure存储账号下N天之前的数据文件-ASM

    ######RemoveStorageBlob*DaysOld##### <# .SYNOPSIS Remove all blob contents from one storage accou ...

  6. D01——C语言基础学PYTHON

    C语言基础学习PYTHON——基础学习D01 20180705内容纲要: 1 PYTHON介绍 2 PYTHON变量定义规则 3  PYTHON文件结构 4 PYTHON语句及语法 5 字符编码 6 ...

  7. (六)Audio子系统之AudioRecord.release

      在上一篇文章<(五)Audio子系统之AudioRecord.stop>中已经介绍了AudioRecord如何暂停录制,接下来,继续分析AudioRecord方法中的release的实 ...

  8. Android的启动模式(下)

    Android中的启动模式(下) 在这篇文章中,我会继续跟大家分享有关于Android中启动模式的相关知识.当然,如果对这个启动模式还不完全了解或者没有听过的话,可以先看看我之前写的有关于这个知识点的 ...

  9. OpenERP 中国财务模块 调整

    最开始的模样是这个样子的 后三行是没用的,于是在RML文件中注释掉相关的代码,改进后的界面如下: 这个样子看起来是好多了,但是 数量跟是十亿千百的那块看起来还是很别扭,调整行高后的结果: 最诡异的事情 ...

  10. [iOS]使用Windows Azure來做iOS的推播通知 (转帖)

    這一篇我們用Windows Azure 的Mobile Service 來實作iOS的推播通知,底下我們分成三個階段來探討如何實作推播通知的服務: 第一階段: 開啓你的Windows Aure服務   ...