一、概念
File能新建、删除、重命名文件和目录,但不能访问文件内容,需要作为参数传递给IO流的构造。
二、普通使用
2.1 创建
| 构造 | public File(String pathname) public File(String parent, String child) public File(File parent, String child) public File(URI uri) |
//拿到外置缓存路径后,拼接文件名
val file = File(externalCacheDir?.absolutePath + "/MyFile.txt")
//拿到外置缓存路径后,拼接文件夹名
val directory = File(externalCacheDir?.absolutePath + "/MyDirectory")
2.2 获取信息
| 文件名 | name |
public String getName() 文件名 |
| nameWithoutExtension |
public val File.nameWithoutExtension: String 文件名,不包含后缀 | |
| extension |
public val File.extension: String 文件的后缀格式,不包含“.”,例如 mp3。 | |
| absoluteFile |
public File getAbsoluteFile() 绝对路径文件名 | |
| 路径 | path |
public String getPath() 路径。 |
| absolutePath |
public String getAbsolutePath() 绝对路径。 | |
| isAbsolute |
public boolean isAbsolute() 是否是绝对路径。 | |
| parent |
public String getParent() 父路径,String形式。 | |
| parentFile |
public File getParentFile() 父路径,File形式。 | |
| 判断 | exists() |
public boolean exists() 是否已存在。 |
| isDirectory |
public boolean isDirectory() 是否是目录。 | |
| canExecute() |
public boolean canExecute() 是否可执行。 | |
| canRead() |
public boolean canRead() 是否可读取。 | |
| canWrite() |
public boolean canWrite() 是否可写入。 | |
| 其它 | length() |
public long length() 文件大小,单位字节,无法返回文件夹大小。 |
| lastModified |
public long lastModified() 表示文件最近修改时间的 long 型数值(从纪元时间 1970年1月1日 00:00:00 GMT 起的毫秒数)。若文件不存在或发生 I/O 错误,则返回 |
val file = File(externalCacheDir?.absolutePath + "/MyFile.txt")
println("文件名:${file.name}") //打印:MyFile.txt
println("绝对文件名:${file.absoluteFile}") //打印:/storage/emulated/0/Android/data/com.jomurphys.dc/cache/MyFile.txt
println("绝对路径名:${file.absolutePath}") //打印:/storage/emulated/0/Android/data/com.jomurphys.dc/cache/MyFile.txt
println("路径:${file.path}") //打印:/storage/emulated/0/Android/data/com.jomurphys.dc/cache/MyFile.txt
println("父目录:${file.parent}") //打印:/storage/emulated/0/Android/data/com.jomurphys.dc/cache
val newFile = File(externalCacheDir?.absolutePath + "/NewName.txt")
val b = file.renameTo(newFile)
println("重命名是否成功:$b,新名称:${file.name}") //打印:false
println("是否存在:${file.exists()}") //打印:false
println("能否执行:${file.canExecute()}") //打印:false
println("可读吗:${file.canRead()}") //打印:dalse
println("可写吗:${file.canWrite()}") //打印:false
println("是目录吗:${file.isDirectory}") //打印:false
println("上一次修改:${file.lastModified()}") //打印:0
println("长度:${file.length()}") //打印:0
2.3 文件夹操作
| mkdirs() | public boolean mkdirs() |
| mkdirs() |
public boolean mkdirs() 创建文件夹,上级路径不存在会一并创建。 |
| list() |
public String[] list() 获取路径中所有文件和文件夹。 |
| listFiles() |
public File[] listFiles() 获取路径中所有文件。 |
val directory = File(externalCacheDir?.absolutePath + "/Dir111/Dir222")
//创建文件夹,上级路径不存在会一并创建
directory.mkdirs()
//获取文件夹中所有文件和文件夹
val list = directory.list()
for (i in list) { println(i) } //打印的是文件名或文件夹名
//获取文件夹中的所有文件
val files = directory.listFiles()
for (file in files) { println(file) } //打印出来的是文件名
三、扩展方法
上面的都是 Java 提供的方法,Kotlin 扩展了一些方法和属性更方便操作。
3.1 获取信息
| extension() |
public val File.extension: String 文件后缀名,不包括那个点。 |
| nameWithoutExtension() |
public val File.nameWithoutExtension: String 文件名,不包括后缀。 |
3.2 遍历
Kotlin把目录遍历这个功能重新梳理了一下,归纳为FileTreeWalk文件树。首先调用File对象的walk方法得到FileTreeWalk实例,接着依次为该实例设置具体的条件,包括遍历深度、是否匹配文件夹、文件扩展名,以及最后的文件队列循环处理。
| walk() | 自上而下,深度优先,遍历整个文件夹。 |
| walkBottomUp() | 自下而上的顺序遍历文件目录和内容。 |
| walkTopDown() | 自上而下的顺序遍历文件目录和内容。 |
//遍历文件
file.parentFile //获取上级目录
.walk() //先访问文件夹,再访问里面的内容
.maxDepth(1)//需遍历的目录层级为1,即无需检查子目录
.filter { it.isFile } //只挑选文件,不处理文件夹
.filter { it.extension in listOf("png","jpg") } //选择扩展名为png和jpg的图片文件
// .filter { it.extension.equals("txt", true) }
.forEach(::println)//循环处理符合条件的文件
//文件拷贝
file.copyTo(File(file.parent, "copy.txt"), true)
//文件夹拷贝
//target:新地址
//overwrite:是否覆盖
//onError:失败的回调
file.parentFile.copyRecursively(
File(file.parentFile.parentFile.parent, "copyRecursively"),
true
)
3.3 简化读取
| 字符 | readText() |
File.readText(charset: Charset = Charsets.UTF_8): String 以 String 形式获取此文件的全部内容,默认编码UTF-8,不要在大文件上使用此方法,上限2G。 |
| readLines() |
public fun File.readLines(charset: Charset = Charsets.UTF_8): List<String> 以 List<String> 形式获取此文件的全部内容,一行内容是一个元素,只能是文本。不要在大文件上使用此方法。 | |
| useLines() |
public inline fun <T> File.useLines(charset: Charset = Charsets.UTF_8, block: (Sequence<String>) -> T): T 拿到所有行的序列,并调用action。 | |
| forEachLine() |
public fun File.forEachLine(charset: Charset = Charsets.UTF_8, action: (line: String) -> Unit): Unit 按行读取文件并为每行调用action,适用于大文件。 | |
| 文件 | readBytes() |
public fun File.readBytes(): ByteArray 以 ByteArray 的形式获取此文件的全部内容,不要在大文件上使用此方法,上限2G。 |
| forEachBlock() |
public fun File.forEachBlock(action: (buffer: ByteArray, bytesRead: Int) -> Unit): Unit 按字节块读取文件并为每个读取的块调用action,字节块默认为4096,最小可为512。适用于大文件。 |
3.4 简化写入
| 字符 | writeText() |
public fun File.writeText(text: String, charset: Charset = Charsets.UTF_8): Unit 写入字符串,已有同名文件会覆盖。 |
|
appendText() |
public fun File.appendText(text: String, charset: Charset = Charsets.UTF_8): Unit 追加字符串到文件末尾。 | |
| 文件 | writeBytes() |
public fun File.appendBytes(array: ByteArray): Unit 写入字节数组,已有同名文件会覆盖。 |
| appendBytes() |
public fun File.writeBytes(array: ByteArray): Unit 追加字节数组到文件末尾。 |
3.5 拷贝
| copyTo() | 复制文件或者文件夹,并且会创建target所需的各个父级文件夹(如果缺少)。overwrite为true时,target可被覆盖,不为true并且当target存在时,返回false,复制失败。overwrite为true并且target是一个文件夹时,只有当文件夹为空时才会被替换。源文件如果是文件夹,则只会创建目标文件夹,不会复制文件夹中的文件。该操作不会保留复制的文件属性,例如创建/修改日期、权限等。 |
| copyRecursively() | 递归复制文件或者文件夹,并且会创建target所需的各个父级文件夹(如果缺少)。如果源文件是文件夹,将复制文件夹中的所有内容。该操作不会保留复制的文件属性,例如创建/修改日期、权限等。默认自带的错误处理器会将错误抛出,可以传入一个Lambda用来处理异常。复制文件夹失败时,可能已经复制了一部分。 |
3.6 删除
| deleteRecursively() |
public fun File.deleteRecursively(): Boolean 递归删除文件或者文件夹,删除文件夹失败时,可能已经删除了一部分。 |
3.7 IO流
|
inputStream() outputStream() | public inline fun File.inputStream(): FileInputStream |
| public inline fun File.outputStream(): FileOutputStream | |
|
reader() writer() |
public inline fun File.reader(charset: Charset = Charsets.UTF_8): InputStreamReader 返回一个InputStreamReader以读取此文件的内容。 |
|
public inline fun File.writer(charset: Charset = Charsets.UTF_8): OutputStreamWriter 返回一个OutputStreamWriter用于写入此文件。 | |
|
bufferedReader() bufferedWriter() |
public inline fun File.bufferedReader(charset: Charset = Charsets.UTF_8, bufferSize: Int = DEFAULT_BUFFER_SIZE): BufferedReader 返回一个BufferedReader用于读取此文件的内容,默认大小8*1024。 |
|
public inline fun File.bufferedWriter(charset: Charset = Charsets.UTF_8, bufferSize: Int = DEFAULT_BUFFER_SIZE): BufferedWriter 返回一个BufferedWriter用于写入此文件。 | |
| printWrite() | public inline fun File.printWriter(charset: Charset = Charsets.UTF_8): PrintWriter |
val file = File("m:\\demo.txt")
fun read() {
println(file.readText()) //段落之间有换行
file.readLines().forEach(::println)
file.forEachLine { println(it) }
file.forEachBlock { buffer, bytesRead -> print(buffer.decodeToString()) }
file.bufferedReader().use { it.readLines().forEach(::println) }
file.inputStream().use {
//一个个读取太慢
// var num: Int
// while (it.read().also { num = it } != -1) {
// print(num.toChar())
// }
// }
val arr = ByteArray(1024)
var num: Int
while (it.read(arr).also { num = it } != -1) {
print(arr.decodeToString(0, num)) //使用num而不是arr.size是因为最后一次读取不一定能读满,不然后面全时空格
// print(String(arr, 0, num)) //效果同上
}
}
}
fun write() {
//覆盖
file.writeBytes("writeBytes\n".toByteArray())
file.writeText("writeText\n")
file.outputStream().use { it.write("outputStream\n".toByteArray()) }
file.printWriter().use { it.println("printlnWriter") }
file.bufferedWriter().use { it.write("bufferedWriter") }
//追加
file.appendBytes("appendBytes".toByteArray())
file.appendText("appendText")
}
本文介绍了Kotlin中如何使用FileTreeWalk进行文件遍历,包括walk、walkBottomUp、walkTopDown方法,以及文件名处理、流读写操作、文件复制、删除和Java流对象的使用。重点讲解了文件过滤、复制、删除的技巧和流处理的最佳实践。
1135





