java 读取文本文件怎么写?附 4 种常用方法+实战场景对比

编程狮(w3cschool.cn) 2026-09-01 10:19:16 浏览数 (21)
反馈

你写好一段 java 读取文件的代码,运行时却抛 FileNotFoundException,或者读出来的内容是空的——这多半不是逻辑错,而是路径写错,或用了过时的 FileInputStream 却忘了关流。最稳妥的写法是 Java 7 之后提供的 Files.readAllBytes(Path),一行就能把整个文件读进内存,再用 new String(bytes, StandardCharsets.UTF_8) 转成文本。本文基于 JDK 17 实测,系统梳理 Files.readAllBytesBufferedReader 逐行读、Files.lines 流式读、以及 Scanner 四种做法,并附编码坑、大文件处理、资源关闭三个实战要点。读完你既能按场景选对方法,也懂什么时候该换方案。今天这篇文章,编程狮就把这块讲透。

Java 读取文件方法图

一、为什么读取文件总踩坑

Java 读文件表面简单,实则有两个高频雷区。第一是相对路径基准new File("data/demo.txt") 里的 data 是「当前工作目录」下的子目录,在 IDE 里跑和打包成 jar 后跑,工作目录往往不同,路径自然就找不到了。第二是字符编码:不显式指定编码时,Java 会用平台默认编码(Windows 是 GBK),读一个 UTF-8 文件必然乱码。

版本提醒:JDK 7 引入的 java.nio.file.Files 是最推荐的入口,老代码的 FileInputStream 容易忘记 close 导致文件句柄泄漏。现代项目应优先用 NIO。

⚠️ 注意:不要手动拼接路径字符串。new File("data/" + name) 在 Linux 上会失败,要用 Paths.get("data", name) 这种平台无关写法,跨环境才不会出事。

1.1 触发条件

什么情况一定会翻车:在 IDE 里能跑、打成 jar 就找不到文件(资源该用 getResourceAsStream);读中文内容出现「锘」或乱码方块;循环里反复 new FileInputStream 没关闭,跑久了报「Too many open files」;读的是 classpath 里的资源却用了文件系统路径。

1.2 常见误解

有人认为「read() 一次就能拿全部内容」。不会。InputStream.read() 单次只读一个字节或一段缓冲,必须循环累加。想一次拿全文本,直接用 Files.readAllBytes 最省心,它内部已经帮你做好了循环读取。

再说一个隐蔽坑:相对路径的基准在不同运行方式下会变。在 IDE 里右键运行,工作目录通常是项目根;用 java -jar 跑,工作目录是你执行命令的目录;放进定时任务(cron)里,工作目录又可能是用户家目录。同一个 Paths.get("data/demo.txt") 三种环境三种结果,这就是「本地能跑、上线就找不到」的根源。稳妥做法是:明确区分「外部文件」(用绝对路径或配置指定)和「打包内资源」(用 getResourceAsStream)。另一个坑是权限:在 Linux 上读 /var/log 或别的用户目录,可能抛 AccessDeniedException,和「文件不存在」是两回事,排错时别只看 FileNotFoundException。还有字符集,Windows 默认 GBK、Linux 默认 UTF-8,跨平台读文本一定要显式传 StandardCharsets.UTF_8,否则同一份文件两边看到的内容不一样。

二、方法一:Files.readAllBytes(最省心)

适合谁:小到中等文件(几 MB 内)、只想拿到完整字符串。代价:会把整个文件读进内存,大文件会触发 OutOfMemoryError,不适合日志类大文本。

  1. Paths.get 构造路径,避免手动拼分隔符;
  2. Files.readAllBytes(path) 一次拿 byte 数组;
  3. StandardCharsets.UTF_8 显式转码,杜绝平台默认编码。

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


Path path = Paths.get("data", "demo.txt");
byte[] bytes = Files.readAllBytes(path);
String text = new String(bytes, StandardCharsets.UTF_8);
System.out.println(text);

上面这段做的是把 data/demo.txt 整个读进 bytes,再按 UTF-8 解码成字符串。执行后你会看到文件原样打印出来。系统入门建议先过一遍 Java 教程 的 IO 章节打底。

三、方法二:BufferedReader 逐行读(内存友好)

适合谁:文件较大、要逐行处理(如日志分析、CSV 清洗)。代价:代码比方法一多几行,但换来的是恒定内存占用。

BufferedReader 带缓冲,每次读一行,不会一次性占满内存,是处理大文本的经典写法。配合 try-with-resources 还能自动关流。

try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);   // 逐行处理
    }
}

语法细节记不住时,翻 Java 速查手册 的 IO 段落最快。这里的 try-with-resources 会自动关流,比手写 finally 更安全,也避免了句柄泄漏。

四、方法三:Files.lines 流式读(函数式)

适合谁:要对行做过滤、计数、收集(如「统计 ERROR 行数」)。代价:它是惰性流,忘了关流仍有句柄风险,建议用 try-with-resources 包起来。

try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
    long count = lines.filter(l -> l.contains("ERROR")).count();
    System.out.println("ERROR 行数:" + count);
}

它返回的是 Stream<String>,可以链式 map/filter,写统计类逻辑特别顺手。注意流必须关闭,否则底层文件句柄会一直占用。

五、方法四与四种方法横向对比

Scanner 是第四种,适合「边读边按分隔符解析」,比如逐段读取并用正则切分,但性能是四种里最弱的,仅推荐在解析简单格式时用。

方法 适用场景 优点 代价
Files.readAllBytes 小文件拿全文本 一行搞定 大文件内存爆
BufferedReader 大文件逐行 内存友好 代码略多
Files.lines 行级统计/过滤 函数式优雅 需包 try 关流
Scanner 边读边解析 能按分隔符切 性能最弱

图:终端里四种读法输出对比(additive,待人工补图)

踩坑清单:

  • 编码必须显式指定:永远传 StandardCharsets.UTF_8,别依赖平台默认。
  • 大文件别用 readAllBytes:超过几百 MB 改用 BufferedReader 或分页读。
  • 资源要关闭:优先 try-with-resources,避免句柄泄漏拖垮进程。
  • 资源文件用 getResourceAsStream:jar 内的文件不能用 Paths.get 读。

实战里还有「边读边写」的场景要小心:比如把 A 文件内容加工后写入 B 文件,务必先确认 B 的父目录存在(Files.createDirectories),否则会抛 NoSuchFileException。读取大目录时,Files.walk 会递归遍历,若目录极深或含符号链接环,可能栈溢出或死循环,记得用 Files.walkFileTree 配合 SimpleFileVisitor 控制行为。异常处理上,IO 操作几乎都抛受检异常 IOException,别用空 catch 吞掉——至少打日志并给出可读的错误信息,否则生产环境文件读失败时你连原因都查不到。最后提醒一句:读网络挂载的文件(NFS / 云盘)比本地磁盘慢且更易超时,关键路径加超时和重试,比盲目重试整个任务更稳。

Java 读取文件的四种方法怎么选

总结

Java 读文件,小文件用 Files.readAllBytes 最省心,大文件用 BufferedReader 逐行最稳,Files.lines 适合做行级统计。核心是路径用 Paths.get、编码显式 UTF-8、资源用 try-with-resources 关闭。想深入 JVM 层面的文件与流机制,可以看 Java 虚拟机教程 补充底层知识。

要点带走:

  • 相对路径以工作目录为基准,跨环境要用 getResourceAsStream 读资源;
  • 四种写法按内存/场景取舍,编码和关流是两个最常翻车的点;
  • 大文件放弃 readAllBytes,逐行或流式处理最稳。

下一步想把 Java 这块学扎实,建议把四种写法都在本地跑一遍:先读个小文件看正常输出,再故意传个错路径记下抛的是什么异常,然后换个含中文的文件试试不指定编码会乱成什么样——亲手踩过一轮,比只看记得牢。

延伸学习

想把这块知识系统补齐,可以按这个顺序来:

  1. 先过一遍 Java 文件读写配套课程,把基础概念铺平;
  2. 语法记不住时,翻 Java 入门学习路线笔记 看学习路线加深印象;
  3. 写完后想随手验证代码,在线运行 Java 代码工具 能直接在浏览器里跑 Java 片段。

常见问题

Q:读 resources 下的文件为什么找不到?

A:打包后文件在 jar 里,不能用 Paths.get 读。要用 getClass().getResourceAsStream("/demo.txt") 以流方式读取,这是 Java 资源加载的固定写法,路径以斜杠开头表示从 classpath 根找。

Q:中文读出来是乱码怎么办?

A:九成是没指定编码。所有读方法都显式传 StandardCharsets.UTF_8,不要依赖平台默认(Windows 默认 GBK 必乱码)。如果源文件本身是 GBK,就要用 StandardCharsets.GBK 去读,编码必须和文件实际编码一致。

Q:几百 MB 的大文件怎么读不爆内存?

A:放弃 readAllBytes,改用 BufferedReader.readLine() 逐行处理,或 Files.lines() 流式消费,处理完一行释放一行,内存占用恒定,不会随文件增大而上涨。

1 人点赞