前言
最近搞了一个文件遍历功能,发现kotlin语言提供了新的函数Walk()
walk() 是 Kotlin 标准库中为 File 类提供的扩展函数,用于遍历文件系统的树形结构。
发现这个函数还是蛮强大的,于是做了个整理
下面介绍它的基本功能和一些使用注意
遍历文件
最基本的遍历文件操作使用很简单,代码如下:
kotlin
File("/path/directory").walk().forEach { file ->
println("${if (file.isDirectory) "目录" else "文件"}: ${file.absolutePath}")
}
这里就可以对整个文件夹的文件做一个遍历,打印出是目录还是文件
遍历方向
walk()函数可以控制遍历方向,从上到下还是从下到上
它传入一个FileWalkDirection枚举类,如下:
kotlin
File(".").walk(FileWalkDirection.TOP_DOWN).forEach { println(it.path) }
这个FileWalkDirection有两个选项:
TOP_DOWN和BOTTOM_UP
其中TOP_DOWN是默认的
过滤文件
有时候我们不需要遍历文件夹下所有文件,而是有一些规则
可以使用filter,如下:
kotlin
// 只获取文件(排除目录)
val filesOnly = File(".").walk()
.filter { it.isFile }
.toList()
限制遍历深度
这里也是一个过滤方式,比如只需要第一层文件,就这样写:
kotlin
// 只遍历当前目录(深度0)
File(".").walk().maxDepth(0).forEach { }
maxDepth可以传入具体的层级:
kotlin
// 遍历当前目录和一级子目录
File(".").walk().maxDepth(1).forEach { }
// 遍历当前目录到三级子目录
File(".").walk().maxDepth(3).forEach { }
当然还可以从第一级子目录开始(跳过当前目录)
kotlin
// 从第一级子目录开始(跳过当前目录)
File(".").walk().minDepth(1).forEach { }
文件查找
查找指定的文件可以这样做:
kotlin
// 查找名为 config.json 的文件
val configFile = File(".").walk()
.firstOrNull { it.name == "config.json" }
// 查找所有 .txt 文件
val textFiles = File(".").walk()
.filter { it.extension == "txt" }
.toList()
或者查找包含特定内容的文件
kotlin
val kotlinFiles = File(".").walk()
.filter { it.isFile }
.filter { file ->
file.readText().contains("fun main()")
}
.toList()
排序和限制
还有一些更灵活的操作:
kotlin
// 按文件大小排序
val sortedBySize = File(".").walk()
.filter { it.isFile }
.sortedBy { it.length() }
.toList()
// 按修改时间排序
val sortedByDate = File(".").walk()
.filter { it.isFile }
.sortedBy { it.lastModified() }
.toList()
// 获取前10个文件
val firstTen = File(".").walk()
.filter { it.isFile }
.take(10)
.toList()
// 跳过前5个文件
val afterFirstFive = File(".").walk()
.filter { it.isFile }
.drop(5)
.toList()
#文件统计
当我们只需要文件数量,或者一些其他的数据,可以这样:
kotlin
// 统计文件数量
val fileCount = File(".").walk()
.count { it.isFile }
// 统计目录数量
val dirCount = File(".").walk()
.count { it.isDirectory }
// 计算总大小
val totalSize = File(".").walk()
.filter { it.isFile }
.sumOf { it.length() }
// 按扩展名分组
val filesByExtension = File(".").walk()
.filter { it.isFile }
.groupBy { it.extension }
// 获取最大的文件
val largestFile = File(".").walk()
.filter { it.isFile }
.maxByOrNull { it.length() }
这里groupBy会返回一个map集合:
Map<K, List>
key是groupBy返回的值,value就是每个组的集合了
访问权限处理
有的文件夹下的文件无法访问,也就是没有权限,这时候可以这样处理:
kotlin
fun safeWalk(directory: File): Sequence<File> {
return try {
directory.walk().onEach {
if (!it.canRead()) {
println("无法访问: ${it.absolutePath}")
}
}
} catch (e: SecurityException) {
println("没有访问权限: ${directory.absolutePath}")
emptySequence()
}
}
使用协程处理大目录
当文件特别多的时候,要考虑性能问题,可以分批次处理:
kotlin
suspend fun processLargeDirectory(directory: File) = withContext(Dispatchers.IO) {
directory.walk()
.chunked(100) // 分批处理,每批100个文件
.forEach { batch ->
// 处理一批文件
batch.forEach { file ->
// 处理每个文件
}
// 可以在这里 yield 一下,避免阻塞
yield()
}
}
注意事项
1:使用 toList() 等终端操作会将所有结果加载到内存,大目录慎用,我们可以用forEach逐个处理文件,不一次性加载到内存
2:walk() 创建的是序列(Sequence),是惰性求值的,适合处理大量文件
3:注意设置合理的 maxDepth,避免栈溢出
4:遍历过程中可能遇到权限问题、文件不存在等异常,需要处理