Java 实例 – 读取文件内容(超详细)

Java 实例 – 读取文件内容:从零开始掌握文件读取操作

在日常开发中,读取文件内容是一个高频需求。无论是加载配置文件、解析日志、处理 CSV 数据,还是读取文本内容做分析,都离不开文件读取这一基础操作。Java 作为一门成熟的语言,提供了多种方式来实现“Java 实例 – 读取文件内容”,从传统的 I/O 流到现代的 NIO.2,各有优劣。本文将带你系统掌握这些方法,适合初学者快速上手,也适合中级开发者查漏补缺。


为什么文件读取如此重要?

想象一下,你的程序就像一个餐厅服务员,而文件就是顾客点的菜单。服务员必须先“读取”菜单内容,才能知道该上什么菜。如果读取错误,上错菜,顾客不满意;如果读取失败,整个服务流程就中断了。

在程序中,文件是数据的“存储仓库”。无论你是读取配置、日志,还是处理用户上传的文本,都必须先“打开”这个仓库,然后“读取”内容。Java 提供了丰富的 API 来完成这项任务,我们接下来一步步来。


使用 FileReader 读取文本文件(基础方式)

FileReader 是 Java 中最简单的字符流读取类之一,适用于读取纯文本文件(如 .txt.log 等)。它继承自 Reader,以字符为单位进行读取,能自动处理编码问题(默认使用系统默认编码)。

import java.io.FileReader;
import java.io.IOException;

public class ReadFileWithFileReader {
    public static void main(String[] args) {
        // 定义要读取的文件路径
        String filePath = "example.txt";

        // 使用 try-with-resources 自动关闭资源
        try (FileReader reader = new FileReader(filePath)) {
            int character; // 用于存储读取的字符(ASCII 或 Unicode 值)

            // 循环读取字符,直到读到文件末尾(-1)
            while ((character = reader.read()) != -1) {
                // 将字符转换为字符类型并打印
                System.out.print((char) character);
            }
            System.out.println("\n文件读取完成。");
        } catch (IOException e) {
            // 捕获异常,提示文件读取失败
            System.err.println("文件读取失败:" + e.getMessage());
        }
    }
}

代码说明:

  • FileReader reader = new FileReader(filePath):创建一个文件读取器,绑定到指定路径。
  • reader.read():每次读取一个字符,返回值为 int 类型,-1 表示文件结束。
  • try-with-resources:自动关闭资源,避免内存泄漏,是现代 Java 推荐做法。
  • (char) character:将 int 类型的字符码转为字符,再打印。

注意:如果文件不存在,会抛出 FileNotFoundException,属于 IOException 的子类。


使用 BufferedReader 提升读取效率

FileReader 每次读一个字符,效率较低。如果文件较大,频繁调用 read() 会成为性能瓶颈。这时,我们可以用 BufferedReader 来“缓冲”读取内容。

BufferedReader 是一个装饰器类,它可以包装 Reader,将数据一次性读入内存缓冲区,减少磁盘 I/O 次数,大幅提升性能。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFileWithBufferedReader {
    public static void main(String[] args) {
        String filePath = "example.txt";

        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line; // 用于存储每一行文本

            // 使用 readLine() 方法按行读取
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            System.out.println("文件按行读取完成。");
        } catch (IOException e) {
            System.err.println("文件读取失败:" + e.getMessage());
        }
    }
}

代码说明:

  • new BufferedReader(new FileReader(filePath)):将 FileReader 包装成缓冲读取器。
  • reader.readLine():每次读取一行,自动跳过换行符,返回 String 类型,当读到文件末尾时返回 null
  • 一行一行读取,适合处理结构化文本(如日志、配置文件)。

小贴士BufferedReader 的缓冲区默认大小为 8192 字节,可自定义,适合中大型文件读取。


使用 Files.readAllLines 读取全部内容(现代推荐)

Java 8 引入了 NIO.2,提供了更简洁的文件操作 API。Files.readAllLines() 是一个非常实用的方法,可以一次性读取整个文件的所有行,返回一个 List<String>

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.io.IOException;

public class ReadFileWithFilesReadAllLines {
    public static void main(String[] args) {
        String filePath = "example.txt";

        try {
            // 读取文件所有行,返回 List<String>
            List<String> lines = Files.readAllLines(Paths.get(filePath));

            // 遍历并打印每一行
            for (String line : lines) {
                System.out.println(line);
            }
            System.out.println("共读取 " + lines.size() + " 行内容。");
        } catch (IOException e) {
            System.err.println("文件读取失败:" + e.getMessage());
        }
    }
}

代码说明:

  • Paths.get(filePath):创建一个路径对象,更安全、可读性更强。
  • Files.readAllLines():一次性读取全部内容,适合小文件(通常 < 100MB)。
  • 返回类型是 List<String>,便于后续处理(如过滤、排序、转换)。

适用场景:读取配置文件、小规模日志、CSV 表格等。

注意:如果文件过大,会占用大量内存,可能导致 OutOfMemoryError。因此,不推荐用于大文件。


处理编码问题:指定字符集

默认情况下,FileReader 使用系统默认编码(如 Windows 上是 GBK,Linux 上是 UTF-8),这可能导致中文乱码。为避免此类问题,应显式指定编码。

import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.nio.file.Files;

public class ReadFileWithEncoding {
    public static void main(String[] args) {
        String filePath = "chinese.txt";

        try {
            // 使用指定编码(UTF-8)读取文件
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(Files.newInputStream(Paths.get(filePath)), StandardCharsets.UTF_8)
            );

            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            reader.close();
        } catch (IOException e) {
            System.err.println("文件读取失败:" + e.getMessage());
        }
    }
}

关键点

  • StandardCharsets.UTF_8:明确指定编码为 UTF-8,避免乱码。
  • Files.newInputStream():NIO.2 提供的高效方法,用于创建输入流。
  • InputStreamReader:将字节流转换为字符流,并指定编码。

建议:所有涉及中文的文件读取,都应显式指定编码,避免“系统默认”带来的不确定性。


实际案例:读取并统计日志文件行数

我们来做一个小实战:读取一个日志文件,统计总行数、包含“ERROR”的行数,并输出结果。

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class LogAnalyzer {
    public static void main(String[] args) {
        String logFilePath = "app.log";

        try {
            List<String> lines = Files.readAllLines(Paths.get(logFilePath));
            AtomicInteger totalLines = new AtomicInteger(0);
            AtomicInteger errorLines = new AtomicInteger(0);

            // 遍历每一行
            lines.forEach(line -> {
                totalLines.incrementAndGet();
                if (line.contains("ERROR")) {
                    errorLines.incrementAndGet();
                }
            });

            System.out.println("总行数:" + totalLines.get());
            System.out.println("错误行数:" + errorLines.get());
            System.out.println("错误率:" + String.format("%.2f%%", (double) errorLines.get() / totalLines.get() * 100));

        } catch (Exception e) {
            System.err.println("日志分析失败:" + e.getMessage());
        }
    }
}

应用场景:日志监控、系统健康检查、自动化运维脚本。


总结:选择合适的读取方式

方法 适用场景 优点 缺点
FileReader + while(read()!=-1) 小文件、字符级处理 简单直观 效率低,不推荐
BufferedReader + readLine() 按行读取,中等文件 高效、易用 需手动管理资源
Files.readAllLines() 小文件、快速读取 代码简洁 内存占用高,不适合大文件
InputStreamReader + StandardCharsets 中文文件、编码安全 防乱码 稍复杂

结语

“Java 实例 – 读取文件内容” 是每个 Java 开发者必须掌握的基础技能。掌握不同方法的适用场景,不仅能写出更高效的代码,还能避免常见的编码问题和内存溢出。从 FileReader 的基础入门,到 BufferedReader 的性能优化,再到 Files.readAllLines() 的现代简洁写法,每一步都在为你的开发之路打下坚实基础。

记住:文件读取不是简单的“打开-读-关闭”,而是一套完整的资源管理与异常处理流程。养成使用 try-with-resources 的习惯,是专业开发者的标志。

下次你再遇到读取配置文件或日志分析任务时,不妨回头看看这篇文章,选择最合适的方式,让代码更健壮、更优雅。