kotlin中的文件和IO流

本文详细介绍了Kotlin中对文件和IO流的操作,包括文件检测、文件夹操作以及Kotlin对File的扩展。重点讲解了如何使用FileWriter、FileReader进行文件读写,以及缓冲流BufferedReader和BufferedWriter的使用,强调了缓冲流在提高读写效率方面的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

absolutePath=/storage/emulated/0/Android/data/zx.com.demo/files/zx1001.txt
path=/storage/emulated/0/Android/data/zx.com.demo/files/zx1001.txt
parent=/storage/emulated/0/Android/data/zx.com.demo/files
重命名是否成功false,name=zx1001.txt

文件检测

val file = File(getExternalFilesDir(“”)?.absolutePath + “/app-debug.apk”)
val dir = File(getExternalFilesDir(“”)?.absolutePath)
println(“exists=” + file.exists())
println(“canExecute=” + file.canExecute())
println(“canRead=” + file.canRead())
println(“canWrite=” + file.canWrite())
println(“isDirectory=” + file.isDirectory)
println(“lastModified=” + Date(file.lastModified()))
println(“length=” + file.length())
println(“---------”)
println(“dir.isDirectory=” + dir.isDirectory)
println(“dir.lastModified=” + Date(dir.lastModified()))
println(“dir.length=” + dir.length())

输出

exists=true
canExecute=false
canRead=true
canWrite=true
isDirectory=false
lastModified=Fri Jun 11 14:05:38 GMT+08:00 2021
length=7785185

dir.isDirectory=true
dir.lastModified=Fri Jun 11 14:13:14 GMT+08:00 2021
dir.length=4096

注意.length()只能返回文件的大小(单位为字节),文件夹的实际大小无法返回。

文件夹操作

val dir = Fi 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》无偿开源 徽信搜索公众号【编程进阶路】 le(getExternalFilesDir(“”)?.absolutePath + “/myDir/dir1”)
dir.mkdirs()//创建一个文件目录。若上层文件目录不存在,一并创建

val appDir = File(getExternalFilesDir(“”)?.absolutePath)
dir.mkdirs()

val files = appDir.listFiles() //返回目录下的文件列表
for (i in files) {
println(i)
}

val filePaths = appDir.list()//返回目录下文件名和文件夹名称数组
for (i in filePaths) {
println(i)
}

输出

/storage/emulated/0/Android/data/zx.com.demo/files/myDir
/storage/emulated/0/Android/data/zx.com.demo/files/zx1001.txt
/storage/emulated/0/Android/data/zx.com.demo/files/app-debug.apk
myDir
zx1001.txt
app-debug.apk

kotlin对File的扩展

以上这些示例中用的都是Java提供的API,kotlin提供了一些扩展方法和扩展属性,可以更加方便的操作文件。

名称作用
extension文件格式的后缀,不包括.,例如:mp3
nameWithoutExtension名称,不包括后缀
appendBytes(ByteArray)追加字节数组到文件末尾
writeBytes(ByteArray)写入字节数组,如果之前有内容,会被覆盖
readBytes(): ByteArray以字节数组的形式获取此文件的全部内容,不建议在大文件上使用此方法,上限2G
appendText(String,Charset)追加字符串到文件末尾,默认是UTF-8编码
writeText(String,Charset)写入字符串,如果之前有内容,会被覆盖
readText(Charset): String以String 形式获取此文件的全部内容,默认编码UTF-8,不建议在大文件上使用此方法,上限2G
reader(Charset): InputStreamReader返回一个FileReader以读取此文件的内容
bufferedReader(Charset, bufferSize: Int): BufferedReader返回一个BufferedReader用于读取此文件的内容
writer(Charset):OutputStreamWriter返回一个FileWriter用于写入此文件
bufferedWriter(Charset, bufferSize: Int): BufferedWriter返回一个BufferedWriter用于写入此文件
forEachBlock(action: (buffer: ByteArray, bytesRead: Int) -> Unit)高阶函数,按字节块读取文件并为每个读取的块调用action,字节块默认为4096。适用于大文件
forEachLine(Charset, action: (line: String) -> Unit)高阶函数,按行读取文件并为每行调用action,适用于大文件
readLines( Charset): List按行读取文件,不要在大文件上使用此方法
copyTo(target: File, overwrite: Boolean = false, bufferSize: Int = DEFAULT_BUFFER_SIZE): File复制文件或者文件夹,并且会创建target所需的各个父级文件夹(如果缺少)。overwrite为true时,target可被覆盖,不为true并且当target存在时,返回false,复制失败。overwrite为true并且target是一个文件夹时,只有当文件夹为空时才会被替换。源文件如果是文件夹,则只会创建目标文件夹,不会复制文件夹中的文件。该操作不会保留复制的文件属性,例如创建/修改日期、权限等
copyRecursively(target: File, overwrite: Boolean = false, onError: (File, IOException))递归复制文件或者文件夹,并且会创建target所需的各个父级文件夹(如果缺少)。如果源文件是文件夹,将复制文件夹中的所有内容。该操作不会保留复制的文件属性,例如创建/修改日期、权限等。默认自带的错误处理器会将错误抛出,可以传入一个Lambda用来处理异常。复制文件夹失败时,可能已经复制了一部分
deleteRecursively()递归删除文件或者文件夹,删除文件夹失败时,可能已经删除了一部分
.walk()自上而下,深度优先,遍历整个文件夹

看完这些扩展方法,瞬间觉得Java不香了,感觉都不需要再学IO流了,但是有时候我们并不是只处理文件的IO流,所以Java的IO流还是要学。

IO流

按照“流”的数据流向,可以将其化分为:输入流和输出流。输入和输出是相对于程序而言,数据流入程序中,那就是输入流,数据流出程序,那就是输出流。按照“流”中处理数据的单位,可以将其区分为:字节流(二进制流)和字符流。在java中,字节是占1个Byte,即8位;而字符是占2个Byte,即16位。一般来说, 对于文本编辑器或文本输出程序创建的文件例如.txt、.xml、.java,应该使用字符流来读取, 对于二进制文件例如png、doc, 应该使用字节流来读取。因为字节流操作的是二进制数据,所以字节流不涉及编码。字符流操作的是字符,而字符是有编码的,所以字符流需要指定编码,下方的例子中都是用的默认的UTF-8编码。

字节流的抽象基类:InputStream,OutputStream。字符流的抽象基类:Reader,Writer。

由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀,如InputStream的子类FileInputStream,Reader的子类FileReader。

表格总结了各种流:

IO流.png

文件流

文件流可以直接操作文件的读写,字符流操作的类为FileWriter和FileReader,字节流操作的类为FileInputStream和FileOutputStream。

FileWriter、FileReader

val fileWriter = FileWriter(file, true)
fileWriter.write(SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(Date()) + “,自己换行\n”)
fileWriter.appendLine(SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(Date()) + “,张三”)
fileWriter.close()

var reader = FileReader(file)
val buf = CharArray(1024)
var len: Int
while (reader.read(buf).also { len = it } != -1) {
println(String(buf, 0, len))
}
reader.close()
println(“-------------”)

//使用扩展方法
reader = FileReader(file)
val lines = reader.readLines()
for (item in lines) {
println(item)
}
println(“-------------”)

//使用扩展方法
reader = FileReader(file)
reader.forEachLine {
println(it)
}
println(“-------------”)

//使用扩展方法
reader = FileReader(file)
reader.useLines {
it.iterator().forEach { it1 ->
println(it1)
}
}

FileWriter的第二个参数,表示是否追加写入,true为追加,false为覆盖。write中可以追加\n实现换行,也可以使用kotlin扩展函数appendLine实现写入一行。读数据时,同样可以使用kotlin扩展函数readLines()、forEachLine、useLines来简化读取,这三种读取方式不需要手动关闭流。最终输出:

2021-06-11 22:43:18,自己换行
2021-06-11 22:43:18,张三

2021-06-11 22:43:18,自己换行
2021-06-11 22:43:18,张三

2021-06-11 22:43:18,自己换行
2021-06-11 22:43:18,张三

2021-06-11 22:43:18,自己换行
2021-06-11 22:43:18,张三

FileInputStream、FileOutputStream

val fileOutputStream = FileOutputStream(file, true)
fileOutputStream.write((SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(Date()) + “,自己换行\n”).toByteArray())
fileOutputStream.write((SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(Date()) + “,李四\n”).toByteArray())

var fileInputStream = FileInputStream(file)
val buffer = ByteArray(1024)
var len: Int
while (fileInputStream.read(buffer).also { len = it } != -1) {
print(String(buffer, 0, len))
}
fileInputStream.close()
println(“-------------”)

//使用扩展方法
fileInputStream = FileInputStream(file)
print(String(fileInputStream.readBytes()))
fileInputStream.close()

使用String的扩展方法toByteArray(),将String转换为字节数组,然后写入到文件。读取时,按块读取,设置块的大小为1024,每读取一块就打印一块。也可以使用InputStreamd类的扩展方法readBytes()一次性读出所有的内容,但是这种方法不适合大文件

缓冲流

为了提高数据读写的速度,JavaAPI提供了带缓冲功能的流,在使用这些流类,会创建一个内部缓冲区数组。缓冲流要“套接”在相应的其他流之上(创建时需要传入其他流),对读写的数据提供了缓冲的功能,提高了读写的效率。写出的数据会先在内存中缓存,使用flush()将会使内存中的数据立刻写出。字符流的缓冲流类为BufferedReader和BufferedWriter,字节流的缓冲流类为BufferedInputStream和BufferedOutputStream。

BufferedReader、BufferedWriter

BufferedReader需要套接在其他的Reader上使用,所以创建时需要传入一个Reader,例如传入一个FileReader,BufferedWriter也是如此。也可以通过Reader和Writer类的扩展函数buffered()返回一个创建好的BufferedReader或BufferedWriter。BufferedReader继承自Reader,所以Reader类的readLines()、forEachLine、useLines扩展函数,BufferedReader同样也可以使用。BufferedReader、BufferedWriter使用示例如下:

var fileWriter = FileWriter(file, true)
val bufferedWriter = BufferedWriter(fileWriter)
bufferedWriter.write(“新增一行\n”)
bufferedWriter.close()

//使用扩展函数buffered获取BufferedWriter,
// 然后用BufferedWriter的扩展函数appendLine添加一行
fileWriter = FileWriter(file, true)
fileWriter.buffered().apply {
appendLine(“扩展函数新增一行”)
close()
}

var fileReader = FileReader(file)
val bufferedReader = BufferedReader(fileReader)
var buffer: CharArray = CharArray(1024)
var len: Int
while (bufferedReader.read(buffer).also { len = it } != -1) {
print(String(buffer, 0, len))
}
bufferedReader.close()
println(“-------------------”)

//使用扩展函数buffered获取BufferedReader,
// 然后用BufferedReader的扩展函数forEachLine逐行获取
fileReader = FileReader(file)
fileReader.buffered().forEachLine {
println(it)
}
println(“-------------------”)

fileReader = FileReader(file)
val lines = fileReader.buffered().readLines()
for (item in lines) {
println(item)
}
println(“-------------------”)

fileReader = FileReader(file)
fileReader.buffered().useLines {
it.iterator().forEach { it1 ->
println(it1)
}
}

输出

新增一行
扩展函数新增一行

新增一行
扩展函数新增一行

新增一行
扩展函数新增一行

新增一行
扩展函数新增一行

可以看出3种扩展函数更简单易用。

下边来对比一下使用了BufferedWriter和不使用BufferedWriter的效率差别,见如下示例:

val time1 = measureTime {
val longFile = File(getExternalFilesDir(“”)?.absolutePath + “/View.java”)
val fileWriter = FileWriter(File(getExternalFilesDir(“”)?.absolutePath + “/View1.txt”), true)
val bufferedWriter = BufferedWriter(fileWriter)
longFile.forEachLine {
bufferedWriter.write(it)
}
bufferedWriter.flush()
bufferedWriter.close()
}
println(“使用BufferedWriter耗时$time1”)

val time2 = measureTime {
val longFile = File(getExternalFilesDir(“”)?.absolutePath + “/View.java”)
val fileWriter = FileWriter(File(getExternalFilesDir(“”)?.absolutePath + “/View2.txt”), true)
longFile.forEachLine {
fileWriter.write(it)
}
e()
}
println(“使用BufferedWriter耗时$time1”)

val time2 = measureTime {
val longFile = File(getExternalFilesDir(“”)?.absolutePath + “/View.java”)
val fileWriter = FileWriter(File(getExternalFilesDir(“”)?.absolutePath + “/View2.txt”), true)
longFile.forEachLine {
fileWriter.write(it)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值