CodeGym /课程 /JAVA 25 SELF /文件重新编码:以一种字符集读取,用另一种字符集写入

文件重新编码:以一种字符集读取,用另一种字符集写入

JAVA 25 SELF
第 37 级 , 课程 4
可用

1. 引言

想象你从会计那里收到一个报表文件,它是用 "Windows-1251"(面向西里尔字母的旧编码)保存的。你的 Java‑应用程序期望所有输入都是 "UTF-8",否则它就会“抱怨”,并把字母显示成乱码。又或者,你要对接一个只接受 "ISO-8859-1" 的系统。怎么办?当然是对文件进行重新编码!

重新编码是指:以一种字符集从文件中读取文本,并将其保存为另一种字符集。这就好比把一封信从一种语言誊抄到另一种语言,以确保收件人准确理解你的意思。

Java 中重新编码如何工作:总体思路

在 Java 中,程序里的字符串(String)始终以 Unicode(UTF‑16)存储。这意味着,当你从文件读取出文本后,它已经是“通用”的,可以写回到任何受支持的编码中。因此,重新编码其实很简单:

  • 按正确的源编码读取文件为字符串。
  • 将这些字符串写入新文件,并显式指定目标编码。

示意图:

[以编码 A 保存的文件] --(使用 Charset A 读取)--> [内存中的 String] --(使用 Charset B 写出)--> [以编码 B 保存的文件]

2. 文件重新编码的分步算法

步骤 1. 确定源编码与目标编码
源编码是指源文件保存时所用的编码(例如 "Windows-1251")。目标编码是你希望得到的结果编码(例如 "UTF-8")。

步骤 2. 以所需编码打开读取流
使用 Files.newBufferedReader(Path, Charset) 或经典的 InputStreamReader

步骤 3. 以所需编码打开写入流
使用 Files.newBufferedWriter(Path, Charset) 或经典的 OutputStreamWriter

步骤 4. 逐行读取并写入新文件
逐行读取(若文件很小也可一次性读取),每一行都写入新文件。

步骤 5. 关闭流(最好使用 try-with-resources)
这样可以确保资源被正确释放。使用 try-with-resources 结构。

3. 代码示例:将 Windows-1251 → UTF-8

我们来实现一个简单程序,把文件从 Windows‑1251 重新编码为 UTF‑8。这个示例在实际工作中很常见,尤其是当你处理俄语数据时。

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;

public class FileReencoder {
    public static void main(String[] args) throws IOException {
        // 文件路径
        Path inputPath = Paths.get("input-1251.txt");
        Path outputPath = Paths.get("output-utf8.txt");

        // 使用 Windows-1251 作为源编码打开 reader
        try (
            BufferedReader reader = Files.newBufferedReader(inputPath, Charset.forName("Windows-1251"));
            BufferedWriter writer = Files.newBufferedWriter(outputPath, StandardCharsets.UTF_8)
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line);
                writer.newLine(); // 别忘了换行!
            }
        }

        System.out.println("文件已成功从 Windows-1251 重新编码为 UTF-8!");
    }
}

这里我们显式指定了读取的编码(Charset.forName("Windows-1251")) 和写入的编码(StandardCharsets.UTF_8)。随后使用 try-with-resources,即使出错也能自动关闭流。writer.newLine() — 用于换行,以保持文件结构。

4. 重要细节与小技巧

如何确定文件的源编码?

  • 文件本身通常不带“标签”标明编码(例外是 BOM,但并不总是存在)。
  • 如果文件是你创建的——使用与写入时相同的编码。
  • 如果是外部来源的文件——尝试用能显示编码的编辑器打开(例如 Notepad++、Visual Studio Code)。
  • 在 Linux 中可以使用命令 file -i filename,但它并不总是能准确判断编码。

如果指定了错误的源编码会怎样?

你会得到乱码或字符丢失。比如,把以 Windows‑1251 保存的文件按 UTF‑8 去读,西里尔字母会变成 "Привет"

异常处理

处理文件时总可能出现错误:文件未找到、无权限、编码不正确。请使用异常处理(try-catch),或向上抛出(throws),以避免应用悄无声息地崩溃。

处理大文件

如果文件非常大(数 GB!),请使用逐行读取与写入,就像上述示例那样。不要把整个文件一次性读入内存——否则会得到 OutOfMemoryError

“在线”重新编码(流式处理)

如果文件特别大,甚至可以不创建中间文件,直接“边读边写”——例如对网络中的数据进行流式处理时。

5. 重新编码文件的常见错误

错误 №1:源编码不正确。如果搞错了源编码,结果会很糟糕:例如 “Hello” 会变成 "Привет"。务必确认文件是用哪种编码创建的。

错误 №2:隐式使用系统默认编码。如果不显式指定编码,Java 会使用系统默认编码(System.getProperty("file.encoding"))。这会在不同计算机上产生不同结果(例如,在 Windows 上是 cp1251,在 Linux 上是 UTF‑8)。

错误 №3:把整个文件读/写到内存。对于大文件,这种做法会导致内存溢出。请使用逐行读写。

错误 №4:未处理的异常。文件可能不存在、被其他进程占用,或者含有损坏的字符。务必处理异常,或使用 try-with-resources

错误 №5:对二进制文件进行重新编码。不要尝试对图片、PDF、压缩包和其它二进制文件进行重新编码!这会破坏它们。重新编码只对文本文件有意义。

1
调查/小测验
和编码打交道第 37 级,课程 4
不可用
和编码打交道
和编码打交道
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION