杰瑞科技汇

Java如何解码Unicode字符串?

这个过程的核心是选择正确的字符集,如果字符集选择错误,就会出现乱码("?????" 或 "����" 等奇怪字符)。

下面我将从最常见的情况开始,详细介绍 Java 中解码 Unicode 的各种方法。


核心概念

  1. 编码: 将字符(抽象的)转换为字节(物理的)的过程。
  2. 解码: 将字节(物理的)转换回字符(抽象的)的过程。
  3. 字符集: 定义了字符与字节序列之间映射关系的规则。
    • UTF-8: 目前最通用、最推荐的编码方式,它使用 1 到 4 个字节来表示一个字符,并且对 ASCII 字符(英文字母、数字)非常友好。
    • UTF-16: Java 内部使用的 Stringchar 类型就是基于 UTF-16 的,它通常使用 2 或 4 个字节表示一个字符。
    • GBK/GB2312: 中文常用的编码,支持简体汉字。
    • ISO-8859-1: 一种单字节编码,不支持中文,常被用作“不安全”的默认编码。

从字节数组解码为 String

这是最常见的情况,例如你从网络请求、文件或数据库中读取到了原始字节数据。

方法 1:使用 String 构造函数(最直接)

Java 提供了可以直接使用 Charset 对象进行解码的构造函数。

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class DecodeFromBytes {
    public static void main(String[] args) {
        // --- 假设这是从某个地方(如文件、网络)读取到的原始字节数组 ---
        // 我们用一个中文字符 "你" 的 UTF-8 编码来模拟
        // "你" in UTF-8 is: 0xE4 0xBD 0xA0
        byte[] utf8Bytes = {(byte) 0xE4, (byte) 0xBD, (byte) 0xA0};
        // --- 1. 使用 UTF-8 字符集进行解码(正确方式)---
        // StandardCharsets.UTF_8 是 Java 7+ 推荐的获取标准字符集的方式
        String correctString = new String(utf8Bytes, StandardCharsets.UTF_8);
        System.out.println("使用 UTF-8 解码结果: " + correctString); // 输出: 你
        // --- 2. 使用错误的字符集解码(GBK)---
        // 这会导致乱码,因为 GBK 会尝试用不同的规则解释这三个字节
        String wrongString = new String(utf8Bytes, Charset.forName("GBK"));
        System.out.println("使用 GBK 解码结果: " + wrongString); // 输出: 鐏� 或其他乱码
        // --- 3. 使用系统默认字符集解码(不推荐!)---
        // 系统默认字符集取决于你的运行环境(操作系统、JVM参数等),非常不可靠
        // String defaultString = new String(utf8Bytes); // 危险!
        // System.out.println("使用默认字符集解码结果: " + defaultString);
    }
}

关键点:

  • 永远不要使用 new String(byte[]) 这种不指定字符集的构造函数,因为它依赖系统默认编码,代码在不同环境下会表现不同,是乱码的主要来源。
  • 始终显式地指定字符集,如 StandardCharsets.UTF_8

从字节流解码为 String

当你处理文件、网络连接等 I/O 操作时,通常会用到 InputStream,直接读取 InputStreambyte[] 可能会消耗大量内存,特别是对于大文件,更好的方式是使用 Reader

方法 2:使用 InputStreamReader(流式解码)

InputStreamReader 是一个字节流到字符流的桥梁,它在内部使用指定的字符集来解码字节,并生成 Reader(字符流)。

import java.io.*;
import java.nio.charset.StandardCharsets;
public class DecodeFromStream {
    public static void main(String[] args) throws IOException {
        // 1. 准备一个包含中文字符的文件 "test.txt",内容为 "你好,世界!"
        // 假设这个文件是用 UTF-8 编码保存的
        File file = new File("test.txt");
        if (!file.exists()) {
            // 如果文件不存在,创建一个用于演示
            try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
                writer.write("你好,世界!");
            }
        }
        // 2. 使用 InputStreamReader 读取并解码
        // try-with-resources 确保流被正确关闭
        try (InputStream inputStream = new FileInputStream(file);
             // 使用 InputStreamReader 指定 UTF-8 字符集来解码字节流
             InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
             BufferedReader bufferedReader = new BufferedReader(reader)) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println("从流中解码出的内容: " + line);
            }
        }
    }
}

关键点:

  • InputStreamReader 是处理字节流并将其解码为字符的首选方式。
  • 它是按需解码的,不会一次性将整个文件读入内存,适合处理大文件。
  • 同样,必须在构造 InputStreamReader 时指定正确的字符集。

处理 URL 编码的字符串

有时,Unicode 字符在 URL 中传输时会被编码成 后跟十六进制数字的形式,"你" 会被编码为 %E4%BD%A0

方法 3:使用 java.net.URLDecoder

URLDecoder 类专门用于解码这种格式。

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
public class DecodeURLEncodedString {
    public static void main(String[] args) throws UnsupportedEncodingException {
        // 一个 URL 编码的字符串
        String encodedString = "%E4%BD%A0%E5%A5%BD%EF%BC%8C%E4%B8%96%E7%95%8C%EF%BC%81";
        // 使用 URLDecoder 进行解码
        // 注意:URLDecoder 内部也需要指定字符集,现代应用推荐使用 UTF-8
        String decodedString = URLDecoder.decode(encodedString, StandardCharsets.UTF_8.name());
        System.out.println("URL 编码字符串: " + encodedString);
        System.out.println("解码后的字符串: " + decodedString); // 输出: 你好,世界!
    }
}

关键点:

  • URLDecoder.decode() 方法需要第二个参数,即解码时使用的字符集。
  • 强烈建议使用 StandardCharsets.UTF_8.name() 作为参数,因为 URL 规范推荐使用 UTF-8。

处理 HTML 实体编码

在 HTML 中,特殊字符(如 <, >, &, 空格)以及非 ASCII 字符有时会用实体编码表示,<&lt; 可能是 &#20320;&#x4F60;

方法 4:使用 org.apache.commons.text.StringEscapeUtils (推荐)

Java 标准库没有直接提供 HTML 实体解码的工具,但 Apache Commons Text 库提供了非常方便的工具。

你需要添加依赖(Maven):

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.10.0</version> <!-- 使用最新版本 -->
</dependency>

然后使用 StringEscapeUtils:

import org.apache.commons.text.StringEscapeUtils;
public class DecodeHTMLEntities {
    public static void main(String[] args) {
        // 包含 HTML 实体编码的字符串
        String htmlEncodedString = "&lt;h1&gt;你好,&amp;世界!&lt;/h1&gt; &#x4F60;&#x597D;";
        // 使用 StringEscapeUtils.unescapeHtml4() 进行解码
        String decodedString = StringEscapeUtils.unescapeHtml4(htmlEncodedString);
        System.out.println("HTML 实体编码字符串: " + htmlEncodedString);
        System.out.println("解码后的字符串: " + decodedString); // 输出: <h1>你好,&世界!</h1> 你好
    }
}

关键点:

  • Java 标准库对此支持不佳,推荐使用成熟的第三方库。
  • StringEscapeUtils 还提供了 unescapeJava(), unescapeXml() 等方法,用于解码其他类型的转义序列。

总结与最佳实践

场景 推荐方法 关键点
byte[] 解码 new String(bytes, StandardCharsets.UTF_8) 永远显式指定字符集,不要用默认的。
InputStream 解码 new InputStreamReader(inputStream, StandardCharsets.UTF_8) 流式处理,内存效率高,适合大文件。
解码 URL 编码字符串 URLDecoder.decode(encodedStr, StandardCharsets.UTF_8.name()) URL 规范推荐 UTF-8,务必指定。
解码 HTML 实体 StringEscapeUtils.unescapeHtml4(htmlStr) (使用 Apache Commons) Java 标准库无直接支持,推荐第三方库。
处理 JSON 使用 JacksonGson 这些库能自动处理 JSON 中的 \uXXXX Unicode 转义序列。

核心原则:

  1. 明确字符集:解码的成败 99% 取决于你是否选择了正确的字符集,当出现乱码时,第一反应就是检查字符集是否匹配。
  2. 优先使用 UTF-8:除非有特殊历史遗留原因,否则在所有新的应用、网络传输和文件存储中,都应将 UTF-8 作为默认和首选的字符集。
  3. 避免默认编码:坚决避免依赖系统默认编码,编写可移植、可预测的代码。
分享:
扫描分享到社交APP
上一篇
下一篇