1. 概述
本文将系统介绍在 Kotlin 中读取文件的常用方法,涵盖以下场景:
- 整个文件内容读取为一个
String
- 按行读取,返回
List<String>
- 从绝对路径或项目资源(resources)中加载文件
这些方法各有适用场景,合理选择能避免踩坑,比如内存溢出或资源未释放等问题。✅
2. 文件读取方法详解
我们先准备一个测试文件 Kotlin.txt
,内容如下:
Hello to Kotlin. It's:
1. Concise
2. Safe
3. Interoperable
4. Tool-friendly
该文件可放在项目目录下或 src/main/resources
中,根据调用方式决定路径写法。
下面逐一介绍不同读取方式。
2.1. forEachLine
—— 行级遍历处理
适用于逐行处理大文件,无需一次性加载到内存。
特点:
- 自动按行切割
- 使用 UTF-8 编码(可指定)
- 每行传入 lambda 处理
- 文件自动关闭
fun readFileLineByLineUsingForEachLine(fileName: String)
= File(fileName).forEachLine { println(it) }
⚠️ 注意:此方法适合“只读+打印/处理”的场景,无法收集成集合返回。
2.2. useLines
—— 安全获取行序列
与 forEachLine
类似,但可以将行数据转换为集合或其他结构,并在结束后自动关闭流。
fun readFileAsLinesUsingUseLines(fileName: String): List<String>
= File(fileName).useLines { it.toList() }
✅ 推荐用于需要按行读取且返回集合的场景,尤其对大文件更安全。
❌ 不要在此 block 外引用 it
(即 Sequence<String>
),否则会抛异常。
2.3. bufferedReader
—— 手动控制缓冲读取
返回标准 Java 的 BufferedReader
,灵活性高,适合复杂逻辑。
fun readFileAsLinesUsingBufferedReader(fileName: String): List<String>
= File(fileName).bufferedReader().readLines()
你也可以手动循环读取:
File(fileName).bufferedReader().use { reader ->
var line: String?
while (reader.readLine().also { line = it } != null) {
println(line)
}
}
✅ 优势:完全掌控读取过程
✅ 结合 use
可确保资源释放
2.4. readLines
—— 简洁读取所有行
最简洁的方式,直接返回 List<String>
:
fun readFileAsLinesUsingReadLines(fileName: String): List<String>
= File(fileName).readLines()
⚠️ 警告:底层是一次性读入全部内容,不适用于大文件(如几百 MB 日志)。
✅ 小文件场景下非常方便,代码干净。
2.5. inputStream
—— 原始字节流操作
通过 InputStream
获取原始字节,再转为字符串:
fun readFileAsTextUsingInputStream(fileName: String): String
= File(fileName).inputStream().readBytes().toString(Charsets.UTF_8)
✅ 适合需要底层控制的场景
❌ readBytes()
会把整个文件加载进内存,大文件有 OOM 风险
建议配合 use
使用以释放资源:
File(fileName).inputStream().use {
it.readBytes().toString(Charsets.UTF_8)
}
2.6. readText
—— 直接读取全文字符串
最简单的整文件读取方式:
fun readFileDirectlyAsText(fileName: String): String
= File(fileName).readText(Charsets.UTF_8)
✅ 一行搞定,适合配置文件、模板等小文本
⚠️ 内部限制最大 2GB,超限会抛异常
❌ 绝对不要用于日志、导出数据等大文件
2.7. getResource
—— 读取 classpath 资源(URL)
用于读取打包后的资源文件(如 jar 内部),基于类路径查找:
fun readFileUsingGetResource(fileName: String): String? {
return this::class.java.getResource(fileName)?.readText(Charsets.UTF_8)
}
示例调用:
readFileUsingGetResource("/Kotlin.txt") // 注意斜杠前缀
✅ 适用于 src/main/resources
下的静态资源
❌ 若文件不存在返回 null
,需判空处理
📌 路径是相对 classpath 的,不是绝对路径
2.8. getResourceAsStream
—— 读取资源流
更推荐的方式,返回 InputStream
,更灵活且兼容性好:
fun readFileAsLinesUsingGetResourceAsStream(fileName: String): List<String>? {
return this::class.java.getResourceAsStream(fileName)
?.bufferedReader()
?.readLines()
}
示例:
readFileAsLinesUsingGetResourceAsStream("/Kotlin.txt")
✅ 推荐用于读取 resources 目录下的文件
✅ 流式处理更省内存
❌ 同样需注意路径写法和空值判断
3. 总结与建议
方法 | 适用场景 | 是否推荐 |
---|---|---|
forEachLine |
逐行处理大文件 | ✅ |
useLines |
按行读取并返回集合 | ✅✅(推荐) |
bufferedReader |
复杂读取逻辑 | ✅ |
readLines |
小文件转 List | ✅(小文件)❌(大文件) |
inputStream + readBytes |
字节级操作 | ⚠️ 注意内存 |
readText |
小文件转 String | ✅(<100MB) |
getResource |
classpath 资源读取 | ✅ |
getResourceAsStream |
推荐的资源读取方式 | ✅✅(首选) |
📌 最佳实践建议:
- 小文件 →
readText()
或readLines()
- 大文件 →
useLines()
或forEachLine()
- 资源文件 →
getResourceAsStream()
- 需要编码控制 → 显式传入
Charsets.UTF_8
等参数
源码地址:GitHub repo