1. 概述
从 Kotlin 1.5 开始,标准库中引入了对 java.nio.file.Path
的一系列扩展函数和操作符。本文将通过实际示例,带你掌握这些 API 的使用方式。
完整的扩展函数列表可查阅官方文档:kotlin.io.path 扩展函数。
这些扩展使得操作文件系统更加符合 Kotlin 风格,推荐优先使用 Path
而非老旧的 File
类。关于两者的对比,可参考文章:Java – Path vs File。
✅ 经验之谈:早年用
File
踩过不少坑,比如路径拼接跨平台问题、异常处理不统一等。现在有了Path
+ Kotlin 扩展,代码清晰又安全。
2. 使用 Path 操作文件系统
2.1 文件与目录的基本操作
Path
是 Java NIO 中表示文件或目录路径的核心类。借助 kotlin.io.path
包中的扩展函数,我们可以非常简洁地完成常见操作。
列出目录内容
Path("~/Downloads").listDirectoryEntries()
该方法返回一个 List<Path>
,包含目标目录下所有子项(文件和子目录)。
复制文件
Path("source.txt").copyTo(Path("destination.txt"))
移动文件(剪切)
Path("source.txt").moveTo(Path("destination.txt"))
⚠️ 注意:默认情况下,moveTo
不会覆盖已存在的目标文件,若需覆盖,传参即可:
Path("source.txt").moveTo(Path("dest.txt"), overwrite = true)
扩展函数背后的原理
以 moveTo
为例,其内部实现如下:
public inline fun Path.moveTo(target: Path, overwrite: Boolean = false): Path {
val options = if (overwrite) arrayOf<CopyOption>(StandardCopyOption.REPLACE_EXISTING) else emptyArray()
return Files.move(this, target, *options)
}
✅ 亮点解析:
- 借助 Kotlin 的默认参数,简化了调用逻辑
- 避免手动构造
StandardCopyOption.REPLACE_EXISTING
数组,提升可读性
相比原生 Java 写法,这简直是降维打击。
2.2 访问嵌套路径
Kotlin 提供了 /
操作符(即 div()
函数),让路径拼接像写 URI 一样自然。
举个实用场景:实现“软删除”功能,把文件移入 .deleted
回收目录:
fun softDelete(path: String) {
val fileToDelete = Path(path)
val destinationDirectory = fileToDelete.parent / ".deleted"
if (destinationDirectory.notExists()) {
destinationDirectory.createDirectory()
}
fileToDelete.moveTo(destinationDirectory / fileToDelete.name)
}
📌 关键点说明:
fileToDelete.parent
获取父目录/ ".deleted"
使用/
拼接新路径notExists()
是exists()
的反向判断扩展createDirectory()
自动创建目录(支持多级)
⚠️ 踩坑提醒:别忘了检查目标目录是否存在,否则
moveTo
可能抛出异常。
3. 文件内容读写操作
3.1 写入文本文件
一次性写入字符串
Path("destination.txt").writeText("<file content>")
默认行为是覆盖已有文件。可通过传参控制行为:
Path("destination.txt").writeText(
text = "<file content>",
charset = Charsets.UTF_8,
StandardOpenOption.CREATE_NEW
)
✅ StandardOpenOption.CREATE_NEW
表示仅当文件不存在时才创建,否则抛出 FileAlreadyExistsException
。
分行写入(适合大文件)
fun writeFileLineByLine(path: String) {
Path(path).bufferedWriter().use { writer ->
(1..10).forEach { i ->
writer.appendLine("Line #$i")
}
}
}
📌 推荐模式:
- 使用
bufferedWriter()
获取带缓冲的写入器 use
确保资源自动关闭(RAII 风格)- 适合逐行生成日志、CSV 等场景
3.2 读取文本文件
一次性读取全部内容为字符串
val content: String = Path("source.txt").readText()
读取为行列表
val lines: List<String> = Path("source.txt").readLines()
⚠️ 注意:此方法会将整个文件加载进内存,不适合大文件!
流式逐行处理(推荐用于大文件)
fun countLines(path: String): Int {
var lines = 0
Path(path).useLines {
it.forEach { _ -> // 我们不需要具体行内容
lines++
}
}
return lines
}
✅ useLines()
特点:
- 内部使用
BufferedReader
,逐行读取 - 自动管理资源释放
- 适用于统计、过滤、转换等流式处理场景
💡 小技巧:配合
Sequence
可实现懒加载处理,进一步优化性能。
4. 递归处理文件
kotlin.io.path
还提供了一些强大的递归操作函数,但请注意:
❌ 当前仍是实验性 API(Kotlin 1.8),未来可能变更
主要函数包括:
Path.walk()
:高效遍历目录树Path.copyToRecursively()
:递归复制整个目录
如何启用实验性 API?
有两种方式:
✅ 方式一:注解标记
@OptIn(ExperimentalPathApi::class)
fun walkDirectory() {
Path("my-folder").walk().forEach { println(it) }
}
✅ 方式二:编译器参数(推荐)
在 build.gradle.kts
中添加:
kotlinOptions {
freeCompilerArgs += "-opt-in=kotlin.io.path.ExperimentalPathApi"
}
📌 替代方案:
如果不想用实验性 API,可以退回到稳定的 File
扩展,详见:Kotlin 递归列出文件
5. 总结
本文介绍了 Kotlin 1.5+ 对 java.nio.file.Path
的扩展能力,核心要点如下:
✅ 优势总结
- 自 Kotlin 1.5 起,
Path
扩展已稳定可用 - 提供了 idiomatic 的文件操作方式,如
/
拼接路径、writeText
/readText
等 - 充分利用 Kotlin 语言特性(默认参数、作用域函数、操作符重载)简化代码
- 推荐替代旧版
File
操作,更安全、更现代
❌ 注意事项
- 递归相关 API 仍处于实验阶段,谨慎用于生产
- 注意资源管理,优先使用
use
或useLines
等自动释放机制
所有示例代码已上传至 GitHub:Baeldung Kotlin 教程仓库