1. 前言
在前面介绍了IO的基本信息以及相关的函数:Kotlin I/O 的基本介绍 (zinyan.com)。而本篇将会基于上篇的介绍结合使用场景,介绍如何进行文件的复制拷贝等操作。
让我们清晰的了解IO函数的基本使用方式。
2. 字节流-文件复制
我们如果要进行文件的复制操作可以使用API提供的FileInputStream
和FileOutputStream
。这两个类都属于底层流,而我们在实际开发中为了提高效率有时会选择BufferedInputStream
和BufferedOutputStream
带有缓冲流的IO类。因为这种缓冲读取的方法可以减少直接读取数据源的次数。
缓冲字节流可以减少I/O操作次数,提高效率。
下面实例进行介绍:
代码语言:javascript复制//介绍文件复制的基本操作
fun main(arg: Array<String>) {
FileInputStream("D:/Text.text").use { fis -> //将D盘的文件复制了一份
FileOutputStream("D:/Text1.text").use { fos ->
val bis = fis.buffered()//创建字节缓冲输入流
val bos = fos.buffered()//创建字节缓冲输出流
bis.copyTo(bos) //将输入流数据拷贝到输出流去
println("复制完毕")
bos.flush()//关闭输出流
}
}
}
在上面的示例中,我将本地D盘的文件进行了拷贝操作。
Test1.text文件不存在,会自动创建一个文件。如果文件已经存在,会覆盖里面的内容。
我们如果只是使用了copyTo后不进行关闭flush的话。那么数据只是缓冲在内存中。还没有复制存储到新文件中。
注意:copyTo函数将输入流复制到输出流,只有当流关闭的时候数据才会被写入到文件中。否则文件大小一直为0。
我们可以通过字节流的方式复制和存储几乎所有类型的文件。但是有些文件本身属于文本类型的。那么我们使用字符流的传输方式是要比字节流快。
(PS:网络数据传输大部分是文本和指令,通过字符流传输。视频等二进制数据通过字节流传输)
3. 字符流-文件复制
字符流就不是Input
和Output
了,而是Reader
(读)和Writer
(写)。
常见方法为:
代码语言:javascript复制//返回字符缓冲区输入流
public fun Reader.buffered(bufferSize: Int = DEFAULT_BUFFER_SIZE): BufferedReader =
if (this is BufferedReader) this else BufferedReader(this, bufferSize)
//返回字符缓冲区输出流
public inline fun Writer.buffered(bufferSize: Int = DEFAULT_BUFFER_SIZE): BufferedWriter =
if (this is BufferedWriter) this else BufferedWriter(this, bufferSize)
//遍历输入流中的每一行数据,对每一行数据都进行处理
public fun Reader.forEachLine(action: (String) -> Unit): Unit = useLines { it.forEach(action) }
// 读取输入流中的数据存储到一个List集合中,每一行数据就是集合的一个item。
public fun Reader.readLines(): List<String> {
val result = arrayListOf<String>()
forEachLine { result.add(it) }
return result
}
//读取输入流中的数据到字符串中,返回一个String对象。
public fun Reader.readText(): String {
val buffer = StringWriter()
copyTo(buffer)
return buffer.toString()
}
实际使用中的示例如下:
代码语言:javascript复制fun main(arg: Array<String>) {
FileReader("D:/Text.text").use { fis ->
FileWriter("./Text1.text").use { fos ->
//创建字符流输入缓冲流
val bis = fis.buffered()
//创建字符流输出缓冲流
val bos = fos.buffered()
//数据复制
val size = bis.copyTo(bos)
println("复制完毕:复制得到的字符数量:$size")
bos.flush()//关闭
}
}
}
在上面的示例中,输出得到的size大小并不是你要拷贝的文件的大小,如果是采用字节流拷贝那么返回的大小就是文件的大小,而字符流拷贝时返回的大小是这个数据的字符大小。
PS:如果字符长度过高超过Long的长度那么这个就是不准确的。
4.File 的扩展
针对File的操作,Kotlin有不少的扩展函数。让我们对文件的各种操作简化了很多。
在这里简单列一下相关的扩展函数。该扩展函数存储在kotlin.io.FilesKt.class
类中。
//读的一些常见函数
//字节数组的形式,返回文件的内容。(可以是视频等多媒体文件)
public fun java.io.File.readBytes(): kotlin.ByteArray
//以字符串的形式,返回文件的内容(默认字符格式是UTF-8)(只能是文本文件)
public fun java.io.File.readText(charset: java.nio.charset.Charset): kotlin.String
//以List集合的形式,返回文件的内容。每一行内容为List的一个Item数据。(只能是文本文件)
public fun java.io.File.readLines(charset: java.nio.charset.Charset ): kotlin.collections.List<kotlin.String>
//写的一些常见函数
//字节数组的形式,写入文件中。(和上面的读取相对应,返回值为空)
public fun java.io.File.writeBytes(array: kotlin.ByteArray): kotlin.Unit
//写入字符串到文件中,默认字符格式编码为utf-8(建议针对文本文件进行操作)
public fun java.io.File.writeText(text: kotlin.String, charset: java.nio.charset.Charset /): kotlin.Unit
//遍历文件中的每一行数据,针对每个数据进行处理(可以批量修改文件内关键字等)
public fun java.io.File.forEachLine(charset: java.nio.charset.Charset , action: (kotlin.String) -> kotlin.Unit): kotlin.Unit
//进行文件的复制操作(target新文件,overwrite:true覆盖目标文件,false不覆盖目标文件(default))
public fun java.io.File.copyTo(target: java.io.File, overwrite: kotlin.Boolean, bufferSize: kotlin.Int ): java.io.File
//遍历文件目录和内容 direction是指遍历的方向(Fiile.walkBottomUp(从下到上),File.walkTopDown(从上到小))
public fun java.io.File.walk(direction: kotlin.io.FileWalkDirection ): kotlin.io.FileTreeWalk
最后,通过遍历并显示目录文件的实例让我们了解一下walk
函数的使用,实例:
fun main(arg: Array<String>) {
File("D://Zinyan")
.walk()//进行遍历目录下的文件和文件夹
.filter { it.isFile } //添加过滤,必须是文件
.filter { it.extension=="class" } //必须是class后缀的文件
.forEach {
println(it) //打印满足条件的对象
}
}
就会输出D盘下的zinyan目录下的所有文件和文件目录。它会遍历到子目录里面去。效果如下:
PS:上面的示例都是用的绝对路径,但是路径也可以填写相对路径。
到这里,基本的IO的读写和存储以及遍历等就介绍完毕了。更深入的就需要我们在实际使用中进行扩展学习了