Java字符串转unicode思路讲解并借Hutool完成实现

一、问题背景

在Java解析XML文件时,遇到一个情况:

<glyph glyph-name="back" unicode="&#59030;" />

比如,上面这个标签的 unicode 属性,在用 SAX 方式解析为字符串后,打印成了一个奇怪的中文:

因此,我希望还原 unicode 的“原码”。

关于 Java 解析 XML 文件的参考
Java文件操作①——XML文件的读取
Java解析XML(4种方式)案例详解

熟悉相关知识的小伙伴,请略过第二节,直接看第三节的源代码!

二、相关知识补充

数字字符引用

一个数字字符引用(Numeric Character Reference)编码是由一个与号(&)跟着一个井号(#),然后跟着这个字符的 Unicode编码值,最后跟着一个分号(;)组成的。

有了数字字符引用,就可以在网页中显示 Unicode 字符了,不用考虑 html 文件本身的编码。

参考自 HTML,Unicode与NCR(数字字符引用)

Unicode

Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

如果把各种文字编码形容为各地的方言,那么 Unicode 就是世界各国合作开发的一种语言。

在这种语言环境下,不会再有语言的编码冲突,在同屏下,可以显示任何语言的内容,这就是 Unicode 的最大好处。 就是将世界上所有的文字用2个字节统一进行编码。那样,像这样统一编码,2个字节就已经足够容纳世界上所有的语言的大部分文字了。

Unicode 的学名是 "Universal Multiple-Octet Coded Character Set",简称为 UCS

早期的 Unicode 标准有 UCS-2UCS-4 的说法。UCS-2 用两个字节编码,UCS-4 用四个字节编码。现在用的是UCS-2,即2个字节编码,而 UCS-4 是为了防止将来2个字节不够用才开发的。

摘抄自 unicode字符集采用几个字节表示一个字符

Utf-8

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8 是 Unicode 的实现方式之一。

UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8 的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

下表总结了编码规则,字母x表示可用编码的位。

Unicode符号范围 (十六进制) UTF-8编码方式 (二进制)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

摘抄自 阮一峰的网络日志 > 字符编码笔记:ASCII,Unicode 和 UTF-8

Unicode 的三种表现形式

unicode 的三种表现形式

格式 举例
1 &# + 十进制数 + ; 例如 &#59030;
2 &#x + 十六进制数 + ; 例如 &#xE696;
3 \u + 十六进制数 例如 \uE696

参考自 unicode 的三种表现形式:&#、&#x、\u

★三、Java字符串转unicode的思路

Java 中的字符串本质上是 char[] 数组,而 Javachar 刚好是 2 个字节,与现行的 Unicode 标准 UCS-2 的字节数相同。

先把字符串分解成一个个 char 字符,再逐个解析 char 字符为 unicode 即可。

需要引入 hutool 依赖:

<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-core</artifactId>
  <version>5.7.22</version>
</dependency>

String转\u格式unicode

以下即 cn.hutool.core.text.UnicodeUtil#toUnicode(java.lang.String, boolean) 的源码:

/**
 * 字符串编码为Unicode形式
 *
 * @param str         被编码的字符串
 * @param isSkipAscii 是否跳过ASCII字符(只跳过可见字符)
 * @return Unicode字符串
 */
public static String toUnicode(String str, boolean isSkipAscii) {
  if (StrUtil.isEmpty(str)) {
    return str;
  }
  final int len = str.length();
  final StrBuilder unicode = StrBuilder.create(str.length() * 6);
  char c;
  for (int i = 0; i < len; i++) {
    c = str.charAt(i);
    if (isSkipAscii && CharUtil.isAsciiPrintable(c)) {
      unicode.append(c);
    } else {
      unicode.append(HexUtil.toUnicodeHex(c)); // 形如 \ue696,刚好占6个字符
    }
  }
  return unicode.toString();
}

参考自 Hutool Unicode编码转换工具-UnicodeUtil

String转&#格式unicode

以下即 cn.hutool.core.text.UnicodeUtil#toUnicode(java.lang.String, boolean) 的源码:

/**
 * 字符串编码为Unicode形式
 *
 * @param str         被编码的字符串
 * @param isSkipAscii 是否跳过ASCII字符(只跳过可见字符)
 * @return Unicode字符串
 */
public static String toUnicode(String str, boolean isSkipAscii) {
  if (StrUtil.isEmpty(str)) {
    return str;
  }
  final int len = str.length();
  final StrBuilder unicode = StrBuilder.create(str.length() * 8);
  char c;
  for (int i = 0; i < len; i++) {
    c = str.charAt(i);
    if (isSkipAscii && CharUtil.isAsciiPrintable(c)) {
      unicode.append(c);
    } else {
      unicode.append("&#").append((int) c).append(";"); // 形如 &#59030;,刚好占8个字符
    }
  }
  return unicode.toString();
}

String转&#x格式unicode

/**
 * 字符串编码为Unicode形式
 *
 * @param str         被编码的字符串
 * @param isSkipAscii 是否跳过ASCII字符(只跳过可见字符)
 * @return Unicode字符串
 */
public static String toUnicode(String str, boolean isSkipAscii) {
  if (StrUtil.isEmpty(str)) {
    return str;
  }
  final int len = str.length();
  final StrBuilder unicode = StrBuilder.create(str.length() * 8);
  char c;
  for (int i = 0; i < len; i++) {
    c = str.charAt(i);
    if (isSkipAscii && CharUtil.isAsciiPrintable(c)) {
      unicode.append(c);
    } else {
      unicode.append("&#x").append(HexUtil.toUnicodeHex(c)).append(";"); // 形如 &#xE696;,刚好占8个字符
    }
  }
  return unicode.toString();
}
posted @ 2022-05-01 16:27  极客子羽  阅读(3160)  评论(0编辑  收藏  举报