Java字符串转unicode思路讲解并借Hutool完成实现
一、问题背景
在Java解析XML文件时,遇到一个情况:
<glyph glyph-name="back" unicode="" />
比如,上面这个标签的 unicode 属性,在用 SAX 方式解析为字符串后,打印成了一个奇怪的中文:
因此,我希望还原 unicode 的“原码”。
关于 Java 解析 XML 文件的参考
Java文件操作①——XML文件的读取
Java解析XML(4种方式)案例详解
熟悉相关知识的小伙伴,请略过第二节,直接看第三节的源代码!
二、相关知识补充
数字字符引用
一个数字字符引用(Numeric Character Reference)编码是由一个与号(&)跟着一个井号(#),然后跟着这个字符的 Unicode编码值,最后跟着一个分号(;)组成的。
有了数字字符引用,就可以在网页中显示 Unicode 字符了,不用考虑 html 文件本身的编码。
Unicode
Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
如果把各种文字编码形容为各地的方言,那么 Unicode 就是世界各国合作开发的一种语言。
在这种语言环境下,不会再有语言的编码冲突,在同屏下,可以显示任何语言的内容,这就是 Unicode 的最大好处。 就是将世界上所有的文字用2个字节统一进行编码。那样,像这样统一编码,2个字节就已经足够容纳世界上所有的语言的大部分文字了。
Unicode 的学名是 "Universal Multiple-Octet Coded Character Set",简称为 UCS。
早期的 Unicode 标准有 UCS-2 、UCS-4 的说法。UCS-2 用两个字节编码,UCS-4 用四个字节编码。现在用的是UCS-2,即2个字节编码,而 UCS-4 是为了防止将来2个字节不够用才开发的。
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 |
Unicode 的三种表现形式
unicode 的三种表现形式
格式 | 举例 | |
---|---|---|
1 | &# + 十进制数 + ; |
例如  |
2 | &#x + 十六进制数 + ; |
例如  |
3 | \u + 十六进制数 |
例如 \uE696 |
★三、Java字符串转unicode的思路
Java 中的字符串本质上是 char[]
数组,而 Java 中 char 刚好是 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();
}
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(";"); // 形如 ,刚好占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(";"); // 形如 ,刚好占8个字符
}
}
return unicode.toString();
}