生僻字处理工具类
对于生僻字的处理可以用到下面相关编码
- 查询汉字对应的编码:https://www.qqxiuzi.cn/bianma/zifuji.php
文章目录
- 生僻字处理
- 概述
- 功能介绍
- 快速开始
- 判断是否是生僻字
- utf8字符串转gbk伪码
- gb18030字符串转gbk伪码
- gbk伪码转utf8
- gbk伪码转gb18030
生僻字处理
概述
- 在系统存储、跨系统报文或文件传输过程中,保证生僻字信息的完整性。
功能介绍
- 通过生僻字工具类,判断传入的字符串中是否包含生僻字。如果包含生僻字,可以把当前字符的字符集(
utf8
或gb18030
)转换成 gbk伪码 的形式来表示,
gbk伪码标识符以前缀为 `H【反引号+H】来区分 - 如:生僻字(“䶮”),转成gbk伪码的形式:`H4DAE,也可以对gbk伪码进行解码操作,解码后就还原当前的字符为:“䶮”
快速开始
- 生僻字工具类主要有如下几个方法
/*** 生僻字工具类** @author xdr630*/
public final class RareCharacterUtils {/*** 生僻字前缀*/private static final String HEX_PREFIX = "`H";private static final Charset GB18030 = Charset.forName("gb18030");private RareCharacterUtils() {}/*** 判断字符串中是否有生僻字** @param text 字符串* @return 结果*/public static boolean containsRareCharacter(String text) {for (int i = 0; i < text.length(); i++) {int c = text.codePointAt(i);if (isRareCharacter(c)) {// 发现生僻字,返回 truereturn true;}//判断是否包含伪码标识, 包含`H,且后续跟进至少两位16进制if (c == '`' && i < text.length() - 3) {int next = text.codePointAt(i + 1);if (next == 'H') {return true;}}}// 没有发现生僻字,返回 falsereturn false;}/*** 判断是否包含生僻字符.** @param c unicode代码点* @return boolean*/private static boolean isRareCharacter(int c) {// 康熙部首、基本汉字补、表情符号?、CJK EXT-B~F、CJK兼容扩展、CJK EXT-Gif ((c >= 0x2F00 && c <= 0x2FDF) || (c >= 0x9FA6 && c <= 0x9FEF) || (c >= 0x10000 && c <= 0x1FFFF) ||(c >= 0x20000 && c <= 0x2F7FF) || (c >= 0x2F800 && c <= 0x2FA1F) || (c >= 0x30000 && c <= 0x3FFFF)) {return true;}// 非生僻字if (c <= 0x7F || c == 0x00B0 || c == 0x00B1 || c == 0x00B7 || (c >= 0x3000 && c <= 0x3003) ||(c >= 0x3005 && c <= 0x3017) || (c >= 0x4E00 && c <= 0x9FA5) || (c >= 0xFF01 && c <= 0xFF5E)) {return false;}// unicode理论最大值U+10FFFFreturn c <= 0x10FFFF;}/*** 字符转gbk伪码,前缀加 `H 标识符** @param text 字符串* @return gbk伪码*/public static String utf8ToGbk(String text) {return charsetToPseudocode(StandardCharsets.UTF_8, text);}/*** 字符转gbk伪码,前缀加 `H 标识符** @param text 字符串* @return gbk伪码*/public static String gb18030ToGbk(String text) {return charsetToPseudocode(GB18030, text);}/*** gbk伪码还原成utf8** @param input gbk伪码字符串* @return utf8编码字符串*/public static String gbkToUtf8(String input) {return gbkPseudocodeToUtf8OrGb18030(input);}/*** gbk伪码还原成gb18030** @param input gbk伪码字符串* @return gb18030编码字符串*/public static String gbkToGb18030(String input) {return gbkPseudocodeToUtf8OrGb18030(input);}/*** 字符转gbk伪码,前缀加 `H 标识符.** @param charset 字符集* @param inputString 字符串* @return gbk伪码*/private static String charsetToPseudocode(Charset charset, String inputString) {if (!charset.canEncode()) {throw new IllegalArgumentException("not support charset: " + charset.name());}int cpCount = inputString.codePointCount(0, inputString.length());StringBuilder sb = new StringBuilder();for (int index = 0; index < cpCount; ++index) {// 获取当前字符的起始位置和下一个字符的位置int i = inputString.offsetByCodePoints(0, index);int nextI = inputString.offsetByCodePoints(0, index + 1);// 获取当前字符的代码点int codepoint = inputString.codePointAt(i);// 获取当前字符String str = inputString.substring(i, nextI);if (containsRareCharacter(str)) {// 如果当前字符是生僻字,转换为伪码,并加上前缀String s = Integer.toHexString(codepoint).toUpperCase();if (s.length() < 5) {// 如果unicode码小于5位,则补足5位s = "0000".substring(0, 5 - s.length()) + s;}sb.append(HEX_PREFIX).append(s);} else {sb.append(str);}}return sb.toString();}/*** gbk伪码转utf8或gb18030** @param gbkPseudocode gbk伪码字符串* @return utf8或gb18030字符串*/private static String gbkPseudocodeToUtf8OrGb18030(String gbkPseudocode) {StringBuilder sb = new StringBuilder();int length = gbkPseudocode.length();int index = 0;while (index < length) {char ch = gbkPseudocode.charAt(index);if (ch != '`') {// 非前缀字符,直接添加到结果中sb.append(ch);index++;continue;}// 如果为反引号字符就+1跳过index++;if (index >= length || gbkPseudocode.charAt(index) != 'H') {// 非生僻字前缀字符,将反引号添加到结果中sb.append('`');if (index < length) {sb.append(gbkPseudocode.charAt(index));index++;}continue;}// 前缀字符 `H` 表示生僻字index++;StringBuilder hexCode = new StringBuilder();if (index < length) {char nextCh = gbkPseudocode.charAt(index);if (Character.isLetterOrDigit(nextCh)) {// 解析生僻字的十六进制编码while (index < length && isHexCode(nextCh)) {hexCode.append(nextCh);// 继续解析下一个字符index++;if (index < length) {nextCh = gbkPseudocode.charAt(index);}// 当unicode长度达到4时,解析判断是否为生僻字if (hexCode.length() < 4) {continue;}// 解决2CC5 'ⳅ'与2CC56 '𬱖' 二义性冲突的问题,优先按5个字节逻辑处理if (hexCode.length() == 4 && index < length && isHexCode(nextCh)) {continue;}int codepoint = Integer.parseInt(hexCode.toString(), 16);if (isRareCharacter(codepoint)) {//判断伪码unicode串是否为为生僻字, 是则转成生僻字字符输出sb.append(Character.toChars(codepoint));hexCode = null;break;}}}}if (hexCode != null) {sb.append("`H").append(hexCode);}}return sb.toString();}private static boolean isHexCode(char ch) {return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f');}}
判断是否是生僻字
- 判断传入的字符串中是否包含生僻字,如果包含生僻字就返回 true,非生僻字就返回 false
public static boolean containsRareCharacter(String text)
- 案例如下:
// 生僻字
Assertions.assertTrue(RareChineseUtils.containsRareCharacter("䶮")); true
Assertions.assertTrue(RareChineseUtils.containsRareCharacter("𬱖")); true
Assertions.assertTrue(RareChineseUtils.containsRareCharacter("崔龿")); true
// 非生僻字
Assertions.assertTrue(RareChineseUtils.containsRareCharacter("!@#$%^&*()_+-=[]{}\\|;':\",./<>?")); false
Assertions.assertTrue(RareChineseUtils.containsRareCharacter("!@#$%^&*()_+-=[]\{}|;':",./<>?")); false
Assertions.assertTrue(RareChineseUtils.containsRareCharacter("你好")); false
utf8字符串转gbk伪码
- 将 UTF-8 编码的字符串转换为 GBK 编码的伪码字符串,以 `H 的形式来标注生僻字的前缀
- 传入 UTF-8 编码的字符串参数,返回 GBK 伪码字符串结果
public static String utf8ToGbk(String text)
- 案例如下:“𬱖” 为生僻字,转码为 `H2CC56 ,"你好 " 为非生僻字就不会转码
hexStringToBytes:将一个十六进制字符串转换为字节数组的方法,将每两个十六进制字符解析成一个字节,并将其存储在字节数组中返回。
String c1 = utf8ToGbk("张𬱖。,,.");
String c2 = utf8ToGbk("你好");
Assertions.assertEquals("张`H2CC56。,,.", c1);
Assertions.assertEquals("你好", c2);String str = new String(hexStringToBytes("E5B494E9BEBF"), StandardCharsets.UTF_8);
System.out.println(str.getBytes());
Assertions.assertEquals("崔龿", str);
Assertions.assertEquals("崔`H9FBF", utf8ToGbk(str));
gb18030字符串转gbk伪码
- 原理和【utf8字符串转gbk伪码】类似,区别在于传入的字符集编码是 gb18030
- 传入 gb18030 编码的字符串参数,返回 GBK 伪码字符串结果
public static String gb18030ToGbk(String text)
- 案例如下:
String str = new String(hexStringToBytes("B4DE82359138"), GB18030);
Assertions.assertEquals("崔龿", str);
Assertions.assertEquals("崔`H9FBF", gb18030ToGbk(str));
gbk伪码转utf8
- 将 GBK 伪码字符串还原成 UTF-8 编码的字符串
- 生僻字转码为 `Hxxx 的形式后,通过该方法把转码后的生僻字解析为 UTF-8 编码的字符串
public static String gbkToUtf8(String input)
- 案例如下:
bytesToHex方法是将字节数组转换为十六进制字符串
String s1 = gbkToUtf8("张`H2CC56。,,.");
String s2 = gbkToUtf8("你好");
Assertions.assertEquals("张𬱖。,,.", s1);
Assertions.assertEquals("你好", s2);String s = gbkToUtf8("崔`H9FBF");
Assertions.assertEquals("E5B494E9BEBF", bytesToHex(s.getBytes(StandardCharsets.UTF_8)));
gbk伪码转gb18030
- 原理和【gbk伪码转utf8】类似
- 将 GBK 伪码字符串还原成 GB18030 编码的字符串
public static String gbkToGb18030(String input)
- 案例如下:
String s1 = gbkToGb18030("张`H2CC56。,,.");
String s2 = gbkToGb18030("你好");
Assertions.assertEquals("张𬱖。,,.", s1);
Assertions.assertEquals("你好", s2);String s = gbkToUtf8("崔`H9FBF");
Assertions.assertEquals("B4DE82359138", bytesToHex(s.getBytes(GB18030)));