1. > 参考的优秀文章

1、十分钟搞清字符集和字符编码

2、Java中byte与16进制字符串的互相转换

3、【异常处理】Incorrect string value: '\xF0\x90\x8D\x83...' for column... Emoji表情字符过滤的Java实现

4、Why a surrogate java regexp finds hypen-minus

2. > 如何检测、替换4个字节的utf-8编码(此范围编码包含emoji)

项目有个需求,是保存从手机端H5页面提交的信息。

大家知道,手机端输入法中经常有自带的表情,其中emoji表情非常流行,如果用户输入emoji表情,由于有部分emoji表情是4个字节的utf-8编码,我们的MySQL数据库在现有版本和编码设置下只能保存3个字节的utf-8编码(如要保存4个字节的utf-8编码则需升级版本和设置另一种编码)。相关信息可见文章《十分钟搞清字符集和字符编码》。

我们的需求不需要支持emoji表情,如果遇到emoji弹出提示或过滤即可。

通过浏览《【异常处理】Incorrect string value: '\xF0\x90\x8D\x83...' for column... Emoji表情字符过滤的Java实现》和《Why a surrogate java regexp finds hypen-minus》,我们得知通过以下代码进行替换:

msg.replaceAll("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]", "");

效果是OK的。

但是,由于能力原因,始终没能理解上述代码十六进制正则表达式的原理,自己写了一端代码来检测、替换4个字节的utf-8编码(但未能经过完整测试,仅用于描述大概思路)。

其中UTF-8编码规则阅读自《十分钟搞清字符集和字符编码》,字节与十六进制的转换参考自《 Java中byte与16进制字符串的互相转换》。

package com.nicchagil.tc.emojifilter;import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;public class UTF8Utils {public static void main(String[] args) {String s = "琥珀蜜蜡由于硬度很低,打磨起来非123常简单,需要的工具也非常简单,自己去买蜜蜡是非常不划算的,完全可以自己磨蜜蜡原石的样子";System.out.println(UTF8Utils.bytesToHex(s.getBytes()));System.out.println(UTF8Utils.bytesToHex(UTF8Utils.remove4BytesUTF8Char(s)));}public static Map<String, Integer> hexMap = new HashMap<String, Integer>();public static Map<String, Integer> byteMap = new HashMap<String, Integer>();static {hexMap.put("0", 2);hexMap.put("1", 2);hexMap.put("2", 2);hexMap.put("3", 2);hexMap.put("4", 2);hexMap.put("5", 2);hexMap.put("6", 2);hexMap.put("7", 2);hexMap.put("c", 4);hexMap.put("d", 4);hexMap.put("e", 6);hexMap.put("f", 8);byteMap.put("0", 1);byteMap.put("1", 1);byteMap.put("2", 1);byteMap.put("3", 1);byteMap.put("4", 1);byteMap.put("5", 1);byteMap.put("6", 1);byteMap.put("7", 1);byteMap.put("c", 2);byteMap.put("d", 2);byteMap.put("e", 3);byteMap.put("f", 4);}/*** 是否包含4字节UTF-8编码的字符(先转换16进制再判断)* @param s 字符串* @return 是否包含4字节UTF-8编码的字符*/public static boolean contains4BytesChar(String s) {if (s == null || s.trim().length() == 0) {return false;}String hex = UTF8Utils.bytesToHex(s.getBytes());System.out.println("full hex : " + hex);String firstChar = null;while (hex != null && hex.length() > 1) {firstChar = hex.substring(0, 1);System.out.println("firstChar : " + firstChar);if ("f".equals(firstChar)) {System.out.println("it is f start, it is 4 bytes, return.");return true;}if (hexMap.get(firstChar) == null) {System.out.println("it is f start, it is 4 bytes, return.");// todo, throw exception for this casereturn false;}hex = hex.substring(hexMap.get(firstChar), hex.length());System.out.println("remain hex : " + hex);}return false;}/*** 是否包含4字节UTF-8编码的字符* @param s 字符串* @return 是否包含4字节UTF-8编码的字符*/public static boolean contains4BytesChar2(String s) {if (s == null || s.trim().length() == 0) {return false;}byte[] bytes = s.getBytes();if (bytes == null || bytes.length == 0) {return false;}int index = 0;byte b;String hex = null;String firstChar = null;int step;while (index <= bytes.length - 1) {System.out.println("while loop, index : " + index);b = bytes[index];hex = byteToHex(b);if (hex == null || hex.length() < 2) {System.out.println("fail to check whether contains 4 bytes char(1 byte hex char too short), default return false.");// todo, throw exception for this casereturn false;}firstChar = hex.substring(0, 1);if (firstChar.equals("f")) {return true;}if (byteMap.get(firstChar) == null) {System.out.println("fail to check whether contains 4 bytes char(no firstchar mapping), default return false.");// todo, throw exception for this casereturn false;}step = byteMap.get(firstChar);System.out.println("while loop, index : " + index + ", step : " + step);index = index + step;}return false;}/*** 去除4字节UTF-8编码的字符* @param s 字符串* @return 已去除4字节UTF-8编码的字符*/public static byte[] remove4BytesUTF8Char(String s) {byte[] bytes = s.getBytes();byte[] removedBytes = new byte[bytes.length];int index = 0;String hex = null;String firstChar = null;for (int i = 0; i < bytes.length; ) {hex = UTF8Utils.byteToHex(bytes[i]);if (hex == null || hex.length() < 2) {System.out.println("fail to check whether contains 4 bytes char(1 byte hex char too short), default return false.");// todo, throw exception for this casereturn null;}firstChar = hex.substring(0, 1);if (byteMap.get(firstChar) == null) {System.out.println("fail to check whether contains 4 bytes char(no firstchar mapping), default return false.");// todo, throw exception for this casereturn null;}if (firstChar.equals("f")) {for (int j = 0; j < byteMap.get(firstChar); j++) {i++;}continue;}for (int j = 0; j < byteMap.get(firstChar); j++) {removedBytes[index++] = bytes[i++];}}return Arrays.copyOfRange(removedBytes, 0, index);}/*** 将字符串的16进制转换为HEX,并按每个字符的16进制分隔格式化* @param s 字符串*/public static String splitForReading(String s) {if (s == null || s.trim().length() == 0) {return "";}String hex = UTF8Utils.bytesToHex(s.getBytes());System.out.println("full hex : " + hex);if (hex == null || hex.length() == 0) {System.out.println("fail to translate the bytes to hex.");// todo, throw exception for this casereturn "";}StringBuilder sb = new StringBuilder();int index = 0;String firstChar = null;String splittedString = null;while (index < hex.length()) {firstChar = hex.substring(index, index + 1);if (hexMap.get(firstChar) == null) {System.out.println("fail to check whether contains 4 bytes char(no firstchar mapping), default return false.");// todo, throw exception for this casereturn "";}splittedString = hex.substring(index, index + hexMap.get(firstChar));sb.append(splittedString).append(" ");index = index + hexMap.get(firstChar);}System.out.println("formated sb : " + sb);return sb.toString();}/*** 字节数组转十六进制* @param bytes 字节数组* @return 十六进制*/public static String bytesToHex(byte[] bytes) {if (bytes == null || bytes.length == 0) {return null;}StringBuilder sb = new StringBuilder();for (int i = 0; i < bytes.length; i++) {int r = bytes[i] & 0xFF;String hexResult = Integer.toHexString(r);if (hexResult.length() < 2) {sb.append(0); // 前补0}sb.append(hexResult);}return sb.toString();}/*** 字节转十六进制* @param b 字节* @return 十六进制*/public static String byteToHex(byte b) {int r = b & 0xFF;String hexResult = Integer.toHexString(r);StringBuilder sb = new StringBuilder();if (hexResult.length() < 2) {sb.append(0); // 前补0}sb.append(hexResult);return sb.toString();}}
package com.nicchagil.tc.emojifilter;public class UTF8HexTester {public static void main(String[] args) {String s = "1";System.out.println("the hex of “" + s + "” : " + UTF8Utils.bytesToHex(s.getBytes()));s = "a";System.out.println("the hex of “" + s + "” : " + UTF8Utils.bytesToHex(s.getBytes()));s = "我";System.out.println("the hex of “" + s + "” : " + UTF8Utils.bytesToHex(s.getBytes()));s = "我很帅";System.out.println("the hex of “" + s + "” : " + UTF8Utils.bytesToHex(s.getBytes()));}}

日志:

the hex of “1” : 31
the hex of “a” : 61
the hex of “我” : e68891
the hex of “我很帅” : e68891e5be88e5b885

3. > 搭建一个测试渠道来测试

由于emoji表情在PC不易输入,最好的输入途径始终在手机上,那么我们搭一个简单的web程序来接收emoji表情吧~

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Emoji</title>
</head>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
<body><form id="myform" action="http://192.168.1.3:8080/emoji/EmojiFilterServlet" >Input parameter : <input type='text' name='msg' /><br/><input type='button' value=' ajax submit ' οnclick="save();" /><input type='submit' value=' form submit ' />
</form></body><script type="text/javascript">function save() {// alert('start save...');var data = $('#myform').serialize();// alert(data);$.ajax({type : "POST",url : "http://192.168.1.3:8080/emoji/EmojiFilterServlet",data : data,success : function(d) {alert(d);}});
}</script></html>
package com.nicchagil.tc.emojifilter;import java.io.IOException;
import java.nio.charset.Charset;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** Servlet implementation class EmojiFilterServlet*/
public class EmojiFilterServlet extends HttpServlet {private static final long serialVersionUID = 1L;/*** @see HttpServlet#HttpServlet()*/public EmojiFilterServlet() {super();// TODO Auto-generated constructor stub}/*** @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)*/protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}/*** @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)*/protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String msg = request.getParameter("msg");System.out.println("msg -> " + msg);}}

转载于:https://my.oschina.net/mic326/blog/1612864

【Java】如何检测、替换4个字节的utf-8编码(此范围编码包含emoji表情)相关推荐

  1. 检测字符串包含emoji表情

    有时候在开发时会遇到不希望字符串中包含emoji表情的情况,Google之后发现了方法,但是似乎iOS9之后的emoji无法过滤,继续寻找方法,在一个NSString的扩展中发现了办法 #import ...

  2. java后台处理APP表情-使用轻量工具emoji-java处理emoji表情字符

    目录 pom依赖 java工具类 测试 Java Url编码转换 在APP开发中,大多需要涉及表情符号丰富APP,但是因为我们的数据库一般是utf8编码,是3个字节,而表情符号基本都是四个字节的uni ...

  3. java过滤ios表情,JS前端去掉emoji表情和Java后台处理emoji表情方法

    莫非定律 : 任何事情都没表面看去来那么简单! emoji表情在项目中使用,因为其特殊的编码格式,经常导致在网络传输.编解码.以及数据入库中带来一些问题! 下面简单介绍使用Js和java处理移除emo ...

  4. JS前端去掉emoji表情和Java后台处理emoji表情方法

    莫非定律 : 任何事情都没表面看去来那么简单! emoji表情在项目中使用,因为其特殊的编码格式,经常导致在网络传输.编解码.以及数据入库中带来一些问题! 下面简单介绍使用Js和java处理移除emo ...

  5. java 替换四个字节的字符 '\xF0\x9F\x98\x84\xF0\x9F)的解决方案

    2019独角兽企业重金招聘Python工程师标准>>> /*** 替换四个字节的字符 '\xF0\x9F\x98\x84\xF0\x9F)的解决方案 ?* @author ChenG ...

  6. java 4字节字符_java 替换四个字节的字符 '\xF0\x9F\x98\x84\xF0\x9F)的解决方案

    /** * 替换四个字节的字符 '\xF0\x9F\x98\x84\xF0\x9F)的解决方案

  7. java 字节替换_java 替换四个字节的字符 apos;\xF0\x9F\x98\x84\xF0\x9F)的解决方案 - 好库文摘...

    /** * 替换四个字节的字符 '\xF0\x9F\x98\x84\xF0\x9F)的解决方案

  8. java 字节替换_java 替换四个字节的字符 '\xF0\x9F\x98\x84\xF0\x9F)的解决方案

    /** * 替换四个字节的字符 '\xF0\x9F\x98\x84\xF0\x9F)的解决方案

  9. 2022-3-26 Java SE检测试卷

    2022-3-26 Java SE检测试卷 一.选择题(30分,每题2分) 二.判断题(20分,每题2分) 三.编程题(50分) 一.选择题(30分,每题2分) 1.java的编程思想是什么? A. ...

最新文章

  1. 难道这是残差结构的本质(addnorm)
  2. Java 面试知识点解析(二)——高并发编程篇
  3. 论文笔记:PointSIFT
  4. 产品经理如何高效的做用户调研?
  5. pwm一个时间单位_详解STM32的PWM输出及频率和脉宽(占空比)的计算——寄存器配置六步曲!...
  6. c#中mysql远程连接方法及实例
  7. python代理池好难啊_新人不会自己搭建代理池?快来引用大佬的
  8. mysql人事管理系统源代码_人事管理系统(源代码.doc
  9. imagemagick, imagick和magickwand编译安装
  10. 项目二:2020年华为软件精英挑战赛
  11. android studio超级玛丽源码,VR版NES模拟器来了,超级玛丽、魂斗罗都支持
  12. Redis安装配置说明
  13. c语言 准确赋值浮点数,C语言浮点数
  14. centos8升级centos stream 8
  15. 研究生毕业2w的工作是什么样的?
  16. oracle怎么算时间,Oracle时间计算
  17. 如何查询个人电脑的最大支持内存?
  18. func() takes 0 positional arguments but 1 was given
  19. 外卖返利系统|外卖返利系统小程序|外卖系统|返利系统
  20. ArcGIS二次开发之一:在ArcMap中启动ArcCatalog的代码实现

热门文章

  1. oracle dbms_crypto,dbms_crypto函数包的简单介绍
  2. mysql 1236错误_mysql报1236错误解决方法
  3. python中xpath使用案例_python爬虫学习笔记:XPath语法和使用示例
  4. 学习博客:关键字package包的使用
  5. Matlab绘图(二)—变色散点图二维、三维绘制
  6. KingbaseES数据库对象管理工具
  7. Could not publish server configuration for Tomcat v8.0 Server at localhost.
  8. QFS文件系统-学习记录
  9. 作计算机报告用英语怎么写,计算机专业英语报告.doc
  10. Kubernetes部署(四):k8s项目交付----(1)持续集成