杰瑞科技汇

linux 中文乱码 java

Java 程序在读取、处理或输出中文时,参与的几个环节(如文件、控制台、JVM 内部)使用了不同的“语言字典”(即字符编码),导致翻译错误,从而显示为乱码。

linux 中文乱码 java-图1
(图片来源网络,侵删)

下面我将从 “为什么会乱码”“如何排查”“如何彻底解决”,为你提供一个全面且系统的解决方案。


核心问题根源:字符编码不一致

想象一个场景:

  1. 你有一个用 UTF-8 编码保存的 你好.txt 文件。
  2. 你的 Linux 终端(Terminal)默认使用 GBK 编码来显示文字。
  3. 你的 Java 程序使用 GBK 编码去读取这个 UTF-8 文件。

这时,Java 用 GBK 的“字典”去读 UTF-8 的“书”,自然就读错了,显示为乱码。

乱码问题通常发生在以下几个环节的编码不匹配:

linux 中文乱码 java-图2
(图片来源网络,侵删)
环节 描述 常见编码
源文件编码 你的 .java 源代码文件本身保存时使用的编码。 UTF-8 (推荐), GBK
JVM 编码 Java 虚拟机在编译和运行时默认使用的编码。 JDK 8 及以前 依赖系统默认,JDK 9+ 默认为 UTF-8
文件/网络编码 程序读取的文件(如 .txt, .properties, .json)或网络请求的编码。 UTF-8 (最常见), GBK, ISO-8859-1
终端/控制台编码 你在 Linux 终端看到程序输出结果时,终端所使用的显示编码。 UTF-8 (现代 Linux), GBK (一些旧系统或中文环境)

只要这四个环节中有任意两个不一致,就可能出现乱码。


如何排查和定位问题?

在解决问题之前,先要学会诊断,用下面的命令来确定你的环境编码。

检查终端/控制台编码

在 Linux 终端中输入:

echo $LANG
echo $LC_CTYPE

输出结果会告诉你当前环境的语言和字符集。

  • en_US.UTF-8:表示环境是英文,使用 UTF-8 编码。
  • zh_CN.GBK:表示环境是中文,使用 GBK 编码。
  • C:这是一个非常经典的“陷阱”,表示使用 ASCII 编码,不支持中文,几乎所有涉及中文的操作都会乱码。

检查 JVM 默认编码

Java 程序启动时,JVM 会从系统环境继承编码,你可以通过一个简单的 Java 程序来查看它:

public class CheckEncoding {
    public static void main(String[] args) {
        System.out.println("file.encoding: " + System.getProperty("file.encoding"));
        System.out.println("sun.jnu.encoding: " + System.getProperty("sun.jnu.encoding"));
    }
}

编译并运行:

javac CheckEncoding.java
java CheckEncoding
  • file.encoding:JVM 默认的文件编码,会影响文件读写。
  • sun.jnu.encoding:JVM 默认的控制台编码,会影响 System.out.println() 的输出。

注意file.encoding 在 JDK 9+ 中默认是 UTF-8,但在 JDK 8 及更早版本中,它通常是系统的默认编码(GBK)。


分类解决方案

根据乱码发生的场景,采取不同的解决方案。

Java 源代码文件(.java)中的中文注释/字符串乱码

问题:编译时或 IDE 中提示乱码。 原因.java 文件的保存编码与编译器期望的编码不一致。 解决方案最佳实践:强制所有 Java 源文件使用 UTF-8 编码。

  1. 保存文件时:在使用 IDE(如 IntelliJ IDEA, Eclipse)时,设置默认编码为 UTF-8。

    • IntelliJ IDEA: File -> Settings -> Editor -> File Encodings -> 将 Global EncodingProject Encoding 都设置为 UTF-8
    • Eclipse: Window -> Preferences -> General -> Workspace -> 将 Text file encoding 设置为 UTF-8
  2. 编译时:如果使用命令行编译,可以显式指定编码:

    # -encoding: 指定源文件使用的编码
    javac -encoding UTF-8 YourFile.java

读取文本文件(如 .txt, .csv)乱码

问题:用 FileReaderBufferedReader 读取文件内容,中文显示为乱码。 原因:代码中未指定编码,使用了 file.encoding(可能是错误的);或者指定的编码与文件实际编码不一致。 错误示范

// 错误:FileReader 使用的是平台默认编码,可能不是 UTF-8
try (FileReader fr = new FileReader("你好.txt")) {
    // ...
}

正确示范永远不要使用 FileReader/FileWriter,它们使用的是平台默认编码,不可靠。 应该使用 InputStreamReader/OutputStreamWriter,并显式指定编码

import java.io.*;
public class ReadFile {
    public static void main(String[] args) {
        // 假设文件是 UTF-8 编码
        String filePath = "你好.txt";
        try (InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "UTF-8");
             BufferedReader br = new BufferedReader(isr)) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

控制台(Terminal)输出乱码

问题System.out.println() 输出的中文显示为乱码。 原因:JVM 内部使用的编码(sun.jnu.encoding)与终端的显示编码不一致。 JVM 用 GBK 编码输出,但终端用 UTF-8 解读,就会乱码。

解决方案

  1. 治本之策:修改 JVM 启动参数 在启动 Java 程序时,通过 -D 参数显式指定 JVM 的编码,使其与终端编码一致。

    # 假设你的终端编码是 UTF-8
    java -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 YourApp
    # 假设你的终端编码是 GBK
    java -Dfile.encoding=GBK -Dsun.jnu.encoding=GBK YourApp

    注意-Dfile.encoding 也会影响文件读写,请确保它与你读取/写入的文件编码匹配。

  2. 治标之策:修改终端编码(不推荐,但有时有效) 你可以临时修改当前终端的编码,让它适应你的程序。

    # 对于支持 locale 的终端,可以临时切换到 GBK 环境
    export LANG=zh_CN.GBK
    export LC_ALL=zh_CN.GBK
    # 然后运行你的 Java 程序
    java YourApp
    # 注意:这只会影响当前终端会话

    这种方法治标不治本,因为你的程序在别的环境可能又会乱。

读取 .properties 配置文件乱码

问题.properties 文件中的中文值读取为乱码(如 \u4f60\u597d)。 原因.properties 文件在 Java 中默认使用 ISO-8859-1 编码读取,如果文件是用 UTF-8 保存的,直接读取就会乱码。

解决方案

  1. JDK 9 及以上版本: 在 java 命令中添加参数 --encoding=UTF-8,这是最简单的方法。

    java --encoding=UTF-8 -jar your-app.jar
  2. JDK 8 及以下版本: 在读取 Properties 对象后,对包含中文的值进行转码。

    import java.io.*;
    import java.util.Properties;
    public class ReadProperties {
        public static void main(String[] args) {
            Properties props = new Properties();
            try (InputStreamReader isr = new InputStreamReader(
                    new FileInputStream("config.properties"), "UTF-8")) {
                props.load(isr);
                // 直接获取可能还是乱码,因为Properties的store/load方法内部有处理
                // 对于JDK 8,如果值是中文,需要额外处理
                String value = props.getProperty("message");
                if (value != null && value.startsWith("\\")) {
                    // 如果是unicode转义序列,需要转换
                    value = unicodeToChinese(value);
                }
                System.out.println("读取到的值: " + value);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 将Unicode转义字符串转换为中文
        public static String unicodeToChinese(String str) {
            StringBuilder sb = new StringBuilder();
            int begin = 0;
            int index = str.indexOf("\\u");
            while (index != -1) {
                sb.append(str.substring(begin, index));
                sb.append((char) Integer.parseInt(str.substring(index + 2, index + 6), 16));
                begin = index + 6;
                index = str.indexOf("\\u", begin);
            }
            sb.append(str.substring(begin));
            return sb.toString();
        }
    }

数据库读写乱码

问题:从 MySQL 数据库读取的中文是乱码,或者存入数据库后变成问号 。 原因:JDBC 连接字符串、数据库服务器、数据表/字段的字符集设置三者不匹配。

解决方案确保整个链路都使用 UTF-8

  1. 数据库层面

    • 创建数据库时指定字符集:CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    • 创建表时指定字符集:CREATE TABLE mytable (...) CHARACTER SET utf8mb4;
    • 确保 MySQL 服务器的配置文件 (my.cnf) 中也设置了 character-set-server = utf8mb4
  2. JDBC 连接字符串层面: 在 JDBC URL 中显式指定编码。

    // 在URL后面添加 ?useUnicode=true&characterEncoding=UTF-8
    String url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8";
    Connection conn = DriverManager.getConnection(url, "user", "password");

终极解决方案与最佳实践

为了避免乱码问题,遵循以下黄金法则:

  1. 统一编码为 UTF-8

    • 所有 Java 源文件 (.java) 保存为 UTF-8 (无 BOM)。
    • 所有配置文件 (.properties, .xml, .json, .txt) 保存为 UTF-8
    • 数据库字段 都使用 utf8mb4 (比 utf8 能更好地支持特殊字符)。
    • JVM 启动参数java -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 ...
    • 终端环境:确保你的 Linux 终端使用 UTF-8 编码 (echo $LANG 查看)。
  2. 永远不要依赖默认编码

    • 在进行文件 I/O、网络 I/O 时,始终显式指定编码(如 StandardCharsets.UTF_8)。
    • 使用 InputStreamReader/OutputStreamWriter 代替 FileReader/FileWriter
    • 使用 StandardCharsets.UTF_8 代替字符串 "UTF-8",可以避免拼写错误。
  3. IDE 和构建工具统一配置

    • 在 Maven 或 Gradle 项目中,可以通过插件统一源文件编码。
      • Maven:
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        </properties>
      • Gradle:
        tasks.withType(JavaCompile) {
            options.encoding = 'UTF-8'
        }

通过以上系统性的方法,你可以 99% 地解决 Linux 环境下的 Java 中文乱码问题,核心思想就是 “一切从源头控制,统一使用 UTF-8”

分享:
扫描分享到社交APP
上一篇
下一篇