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 的习惯,是专业开发者的标志。
下次你再遇到读取配置文件或日志分析任务时,不妨回头看看这篇文章,选择最合适的方式,让代码更健壮、更优雅。