杰瑞科技汇

Java中GBK与UTF-8编码如何转换?

Java 内部处理字符串时,统一使用 UTF-16 编码,我们通常所说的“转换”,指的是在进行 I/O 操作(如读写文件、网络传输)时,如何正确地处理字节流和字符流之间的编码/解码。

Java中GBK与UTF-8编码如何转换?-图1
(图片来源网络,侵删)

核心概念:编码与解码

  • 编码:将字符(String)转换为字节(byte[]),将一个包含中文字符的字符串,按照 GBK 规则转换成一串字节。
  • 解码:将字节(byte[])转换为字符(String),将一串按照 GBK 规则编码的字节,还原成原始的中文字符串。

如果编码和解码使用的字符集不一致,就会出现乱码。


场景一:在内存中进行字符串编码转换

这是最常见的情况,比如你从一个 GBK 编码的文件中读取了字节,需要将其转换成 Java 的 String 对象,然后再将其以 UTF-8 编码写入另一个文件。

步骤:

  1. 解码:使用 String 的构造方法,将 GBK 编码的字节数组解码成 String
  2. 编码:使用 StringgetBytes() 方法,将 String 按照 UTF-8 编码转换成字节数组。

示例代码:

import java.nio.charset.StandardCharsets;
public class EncodingConversion {
    public static void main(String[] args) {
        // 1. 模拟一个从 GBK 编码文件中读取到的字节数组
        // "你好,世界" 这几个汉字用 GBK 编码占 10 个字节
        String originalText = "你好,世界";
        byte[] gbkBytes = originalText.getBytes(StandardCharsets.GBK); // 编码成 GBK 字节
        System.out.println("原始字符串: " + originalText);
        System.out.println("GBK 编码后的字节数组 (十六进制): " + bytesToHex(gbkBytes));
        System.out.println("-------------------------------------");
        // 2. 将 GBK 字节数组解码成 Java String 对象
        // 这是关键一步:告诉 JVM 这些字节是 GBK 编码的,请按这个规则解析
        String decodedString = new String(gbkBytes, "GBK");
        System.out.println("使用 GBK 解码后的字符串: " + decodedString);
        System.out.println("解码后的字符串是否与原始字符串相等: " + originalString.equals(decodedString));
        System.out.println("-------------------------------------");
        // 3. 将这个 String 对象重新编码成 UTF-8 字节数组
        byte[] utf8Bytes = decodedString.getBytes(StandardCharsets.UTF_8); // 编码成 UTF-8 字节
        System.out.println("使用 UTF-8 编码后的字节数组 (十六进制): " + bytesToHex(utf8Bytes));
        System.out.println("-------------------------------------");
        // 4. (可选) 将 UTF-8 字节数组解码回 String,验证结果
        String finalString = new String(utf8Bytes, StandardCharsets.UTF_8);
        System.out.println("从 UTF-8 字节解码回的字符串: " + finalString);
    }
    // 一个辅助方法,用于将字节数组打印成十六进制格式,方便查看
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString();
    }
}

代码解释:

  • originalText.getBytes(StandardCharsets.GBK):将字符串 originalText 按照 GBK 规则编码成字节数组 gbkBytes
  • new String(gbkBytes, "GBK"):这是解码操作,它告诉 Java,“gbkBytes 这个数组里的字节是按照 GBK 编码的,请把它们还原成字符”,得到的就是一个正常的 Java String 对象 decodedString
  • decodedString.getBytes(StandardCharsets.UTF_8):将 decodedString 这个字符串按照 UTF-8 规则重新编码成新的字节数组 utf8Bytes
  • 注意:从 Java 7 开始,推荐使用 StandardCharsets 枚举来指定字符集,如 StandardCharsets.UTF_8StandardCharsets.GBK,这种方式比直接传入字符串(如 "UTF-8")更安全,因为它能避免拼写错误导致的 UnsupportedEncodingException

场景二:文件读写时的编码转换

这是编码转换最实际的应用场景,我们需要确保读取文件时使用正确的源编码,写入文件时使用正确的目标编码。

Java中GBK与UTF-8编码如何转换?-图2
(图片来源网络,侵删)

示例代码:将一个 GBK 编码的文件 input_gbk.txt 复制成一个 UTF-8 编码的文件 output_utf8.txt

import java.io.*;
public class FileEncodingConverter {
    public static void main(String[] args) {
        // 定义源文件和目标文件路径
        File gbkFile = new File("input_gbk.txt");
        File utf8File = new File("output_utf8.txt");
        // 使用 try-with-resources 语句,确保流被自动关闭
        try (
            // 1. 创建 GBK 编码的输入流
            // InputStreamReader 是字节流到字符流的桥梁,可以指定编码
            FileInputStream fis = new FileInputStream(gbkFile);
            InputStreamReader isr = new InputStreamReader(fis, "GBK");
            // 2. 创建 UTF-8 编码的输出流
            // OutputStreamWriter 是字符流到字节流的桥梁,可以指定编码
            FileOutputStream fos = new FileOutputStream(utf8File);
            OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
            // 为了方便按行读写,可以包装成 BufferedReader 和 BufferedWriter
            BufferedReader reader = new BufferedReader(isr);
            BufferedWriter writer = new BufferedWriter(osw)
        ) {
            String line;
            // 逐行读取
            while ((line = reader.readLine()) != null) {
                // 将读取到的行(已经是String)写入到UTF-8编码的输出流中
                writer.write(line);
                // 写入换行符
                writer.newLine();
            }
            System.out.println("文件转换成功!已将 " + gbkFile.getName() + " 从 GBK 转换为 " + utf8File.getName());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码解释:

  • InputStreamReader(fis, "GBK"):这是关键,它包装了一个 FileInputStream(字节流),并指定了读取字节时使用的字符集为 GBK,这样,reader.readLine() 返回的就是一个解码后的、正确的 String
  • OutputStreamWriter(fos, StandardCharsets.UTF_8):同样关键,它包装了一个 FileOutputStream(字节流),并指定了将字符写入文件时要使用的字符集为 UTF-8,当你调用 writer.write(line) 时,OutputStreamWriter 会自动将 String 按照 UTF-8 规则编码成字节,再写入文件。

常见问题与最佳实践

问题1:如何判断一个文本文件是什么编码?

答案:没有 100% 准确的方法。

  • 简单方法:用记事本等编辑器打开,看是否乱码,如果不乱码,很可能是系统默认编码(Windows下是GBK,macOS/Linux下是UTF-8)。
  • 专业方法:使用工具,如 Notepad++(可以检测编码)、VS Code(在右下角显示编码)、或者专门的库如 juniversalchardetTika 等,它们通过分析字节的频率和特征来猜测编码。

问题2:UnsupportedEncodingException 异常

  • 原因:你请求的字符集在当前 Java 运行环境中不被支持。
  • 解决方案
    1. 优先使用 StandardCharsetsUTF-8, ISO-8859-1, US-ASCII 是 Java 必须支持的,不会抛出此异常。
    2. 对于 GBKGBK 是 Java 标准库支持的,所以直接写 "GBK" 通常没问题,但如果环境特殊(如某些嵌入式JRE),也可能不支持,最稳妥的方式是使用 Charset.forName("GBK"),它会在运行时检查,如果不存在会抛出 IllegalCharsetNameExceptionUnsupportedCharsetException
  1. 内部统一:在 Java 程序内部,所有 String 的处理都无需关心编码,Java 会用 UTF-16 处理它们。
  2. I/O 明确:只在涉及 I/O 操作(文件、网络、控制台)时,才明确指定编码。
  3. 首选 StandardCharsets:对于 UTF-8, ISO-8859-1 等标准编码,使用 StandardCharsets.UTF_8,代码更健壮、可读性更高。
  4. 流处理:使用 InputStreamReaderOutputStreamWriter 作为桥梁,将字节流和指定编码的字符流连接起来。
  5. 不要用系统默认编码:避免使用 new String(byte[])String.getBytes() 不带参数的重载方法,因为它们依赖于系统的 file.encoding 属性,会导致程序在不同环境下行为不一致,是乱码的主要来源之一。
Java中GBK与UTF-8编码如何转换?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇