Java 字符串的内部表示
一个非常重要的概念是:Java 字符串(String)在内部就是使用 UTF-16 编码存储的,这意味着,一个 char 变量通常可以表示一个 Unicode 字符(码点)。
- 对于大多数常见的字符(包括中文),码点范围在
\u0000到\uffff之间,可以用一个char表示。 - 但对于一些不常用的字符(如某些生僻字、表情符号),码点范围在
\uffff到0x10ffff之间,需要用两个char(一个“代理对”,surrogate pair)来表示。
我们通常说的“转 Unicode”,就是遍历字符串中的每一个字符码点,并将其格式化为 \uxxxx 的形式。
最推荐的方法(处理所有字符,包括生僻字)
这种方法使用 codePointAt() 来获取每个字符的完整码点,然后使用 Integer.toHexString() 进行格式化,它能正确处理所有 Unicode 字符,包括需要代理对的生僻字。
代码实现
public class ChineseToUnicode {
/**
* 将字符串转换为 Unicode 码点表示形式 ( \u4e2d\u6587)
* @param str 要转换的字符串
* @return 转换后的 Unicode 字符串
*/
public static String stringToUnicode(String str) {
if (str == null) {
return null;
}
StringBuilder sb = new StringBuilder();
// 遍历字符串中的每一个码点
for (int i = 0; i < str.length(); i++) {
int codePoint = str.codePointAt(i);
// 处理代理对(即需要两个 char 表示的字符)
if (Character.isSupplementaryCodePoint(codePoint)) {
i++; // 跳过下一个 char
}
sb.append(String.format("\\u%04x", codePoint));
}
return sb.toString();
}
public static void main(String[] args) {
String chineseStr = "中文测试";
String unicodeStr = stringToUnicode(chineseStr);
System.out.println("原始字符串: " + chineseStr);
System.out.println("Unicode 字符串: " + unicodeStr);
// 包含生僻字的例子
String rareCharStr = "𠮷𠮷"; // 这两个字是生僻字,需要代理对表示
String rareUnicodeStr = stringToUnicode(rareCharStr);
System.out.println("\n原始字符串 (生僻字): " + rareCharStr);
System.out.println("Unicode 字符串 (生僻字): " + rareUnicodeStr);
}
}
代码解释
StringBuilder sb: 用于高效地拼接字符串,避免在循环中频繁创建String对象。str.codePointAt(i): 这是核心方法,它返回从指定索引i开始的字符的 Unicode 码点,对于普通字符,它返回一个char的值;对于需要代理对的字符,它返回完整的码点(一个int类型)。Character.isSupplementaryCodePoint(codePoint): 判断这个码点是否是一个“补充字符”,即是否需要两个char来表示。if (Character.isSupplementaryCodePoint(codePoint)) { i++; }: 如果是补充字符,我们需要将索引i加一,跳过组成这个字符的第二个char,这确保了我们能正确地遍历整个字符串。String.format("\\u%04x", codePoint): 这是格式化字符串的关键。\\u: 一个反斜杠加u,这是 Unicode 转义序列的前缀,因为\是转义字符,所以需要写成\\来表示一个字面的反斜杠。%04x: 这是一个格式说明符。%x: 表示将整数格式化为十六进制小写字母。04: 表示至少占 4 位,如果不足 4 位,则在前面补零,这对于保证\u后面总是有 4 位十六进制数非常重要(中的码点是4e2d,而不是4e2d或4e2d)。
输出结果
原始字符串: 中文测试
Unicode 字符串: \u4e2d\u6587\u6d4b\u8bd5
原始字符串 (生僻字): 𠮷𠮷
Unicode 字符串 (生僻字): \u20a3b\u20a3b
(注意: 的实际码点是 0x20A3B)
简单方法(仅适用于 BMP 字符)
如果你的字符串中只包含基本多语言平面的字符(即所有常见的中文、英文、日文、韩文等,但不包含生僻字、表情符号等),可以使用更简单的方法,这种方法直接遍历 char 数组。
代码实现
public class SimpleChineseToUnicode {
/**
* 将字符串转换为 Unicode 码点表示形式 (简单版)
* 注意:此方法不适用于需要代理对的字符(如生僻字、表情符号)。
* @param str 要转换的字符串
* @return 转换后的 Unicode 字符串
*/
public static String stringToUnicodeSimple(String str) {
if (str == null) {
return null;
}
StringBuilder sb = new StringBuilder();
for (char c : str.toCharArray()) {
sb.append(String.format("\\u%04x", (int) c));
}
return sb.toString();
}
public static void main(String[] args) {
String chineseStr = "中文测试";
String unicodeStr = stringToUnicodeSimple(chineseStr);
System.out.println("原始字符串: " + chineseStr);
System.out.println("Unicode 字符串: " + unicodeStr);
// 这个方法会错误地处理生僻字
String rareCharStr = "𠮷";
String rareUnicodeStr = stringToUnicodeSimple(rareCharStr);
System.out.println("\n原始字符串 (生僻字): " + rareCharStr);
System.out.println("Unicode 字符串 (生僻字, 错误): " + rareUnicodeStr);
}
}
代码解释
str.toCharArray(): 将String转换为一个char数组。for (char c : ...): 遍历每一个char。(int) c: 将char强制转换为int,得到它的码点值。
输出结果
原始字符串: 中文测试
Unicode 字符串: \u4e2d\u6587\u6d4b\u8bd5
原始字符串 (生僻字): 𠮷
Unicode 字符串 (生僻字, 错误): \ud842\udf3b
注意,对于生僻字 ,这个方法输出了它的代理对 0xd842 和 0xdf3b,而不是完整的码点 0x20a3b。不推荐在生产环境中使用这种方法,除非你 100% 确定你的字符串不包含任何 BMP 之外的字符。
总结与对比
| 特性 | 方法一 (推荐) | 方法二 (简单) |
|---|---|---|
| 核心方法 | codePointAt() |
toCharArray() |
| 处理范围 | 所有 Unicode 字符 (包括生僻字、表情符号) | 仅 BMP 字符 (常见字符) |
| 正确性 | 高,能正确处理代理对 | 低,会错误拆分代理对 |
| 代码复杂度 | 稍高,需要处理代理对逻辑 | 非常简单 |
| 适用场景 | 通用场景,任何可能包含特殊字符的字符串 | 已知字符串内容仅限于 BMP 字符的特定场景 |
在进行任何“字符串转 Unicode”的操作时,请优先选择方法一,它更健壮、更通用,能够处理所有可能的字符,避免因生僻字或特殊符号导致的潜在问题,方法二虽然简单,但存在缺陷,仅适用于非常受限的环境。
