kotlin中的文件和IO流

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 = File(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)
}
fileWriter.close()
}
println(“不使用的情况耗时$time2”)

输出

使用BufferedWriter耗时102ms
不使用的情况耗时351ms

示例中我将Android的View的源码的2倍作为数据源,然后分别用FileWriter和BufferedWriter将数据源复制到另外两个TXT中。这里用View的源码的2倍作为数据源是因为View的源码只有1M多,体现不出差异,所以复制了一遍。

输出可以看出,使用BufferedWriter的耗时是不使用时的1/3不到,如果文件再大一些,差异可能更明显。为什么使用缓冲后能提升效率呢,简单理解就是:使用缓冲处理流包装就是一堆一堆的干活,不用CPU多次处理数据转换,只是设置一下数据转换成功后的文件。不使用缓冲处理流包装就是CPU傻傻的一个字节一个字节循环来干活存储写入文件中,相比可见效率明显变慢。所以建议总是使用缓冲流

BufferedInputStream、BufferedOutputStream

类似于BufferedReader、BufferedWriter,可以使用InputStream/OutputStream的buffered()扩展函数获取对应的BufferedInputStream、BufferedOutputStream,贴上示例代码:

var fileOutputStream = FileOutputStream(file, true)
val bufferedOutputStream = BufferedOutputStream(fileOutputStream)
bufferedOutputStream.write(“这是一行数据\n”.toByteArray())
bufferedOutputStream.close()

fileOutputStream = FileOutputStream(file, true)
fileOutputStream.buffered().apply {
write(“扩展函数新增的数据\n”.toByteArray())
close()
}

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

//使用扩展方法buffered()获取BufferedInputStream,
//再调用BufferedInputStream的readBytes()扩展方法,一次获取所有的数据
fileInputStream = FileInputStream(file)
fileInputStream.buffered().apply {
print(String(readBytes()))
close()
}

对象流

DataInputStream类和 DataOutputStream 类可以实现基本数据类型与字符串的输入和输出。 而 ObjectlnputStream 类和 ObjectOutputStream 类除了可以实现基本数据类型与字符串的输入和输出之外, 还可以实现对象的输入和输出。 由于 ObjectlnputStream 类和ObjectOutputStream 类包含 DatalnputStream 类和DataOutputStream 类的所有功能, 所以, 完全可以用 ObjectlnputStream 类和 ObjectOutputStream 类代替 DatalnputStream 类和DataOutputStream 类 。

序列化

并不是每一个对象都可以写到输出流,可以写入到输出流中的对象称为可序列化的。对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。如果需要让某个对象支持序列化机制,则必须让其类是可序列化的为了让某个类是可序列化的,该类必须实现Serializable或者Externalizable。

学习分享,共勉

Android高级架构师进阶之路

题外话,我在阿里工作多年,深知技术改革和创新的方向,Android开发以其美观、快速、高效、开放等优势迅速俘获人心,但很多Android兴趣爱好者所需的进阶学习资料确实不太系统,完整。今天我把我搜集和整理的这份学习资料分享给有需要的人

  • Android进阶知识体系学习脑图

  • Android进阶高级工程师学习全套手册

  • 对标Android阿里P7,年薪50w+学习视频

  • 大厂内部Android高频面试题,以及面试经历


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
6wkmSlKH-1715670333245)]

  • Android进阶高级工程师学习全套手册

[外链图片转存中…(img-Hb7OMEi2-1715670333246)]

  • 对标Android阿里P7,年薪50w+学习视频

[外链图片转存中…(img-8WxRcDHf-1715670333248)]

  • 大厂内部Android高频面试题,以及面试经历

[外链图片转存中…(img-55JZKaXJ-1715670333250)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 29
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Kotlin ,可以使用以下几种方式来读取文件: 1. 使用 `BufferedReader`: ```kotlin val reader = BufferedReader(FileReader("file.txt")) val line = reader.readLine() reader.close() ``` 2. 使用 `Scanner`: ```kotlin val scanner = Scanner(File("file.txt")) while (scanner.hasNextLine()) { val line = scanner.nextLine() // do something with the line } scanner.close() ``` 3. 使用 `InputStream` 和 `BufferedReader`: ```kotlin val inputStream = FileInputStream("file.txt") val reader = BufferedReader(InputStreamReader(inputStream)) val line = reader.readLine() inputStream.close() reader.close() ``` 4. 使用 Kotlin 标准库的 `readText` 函数: ```kotlin val text = File("file.txt").readText() ``` 请注意,在 Kotlin ,打开的文件流应该在使用完毕后关闭,以避免资源泄露。 ### 回答2: Kotlin可以使用以下几种方式来读取文件: 1. 使用Java的IO类库:Kotlin可以直接使用Java的IO类库来读取文件,例如使用FileInputStream、BufferedReader等类。可以通过创建File对象指定文件路径,然后使用FileInputStream来读取文件的内容。 ```kotlin import java.io.File import java.io.BufferedReader import java.io.FileInputStream fun main() { val file = File("path/to/file.txt") val fis = FileInputStream(file) val reader = BufferedReader(InputStreamReader(fis)) var line: String? while (reader.readLine().also { line = it } != null) { println(line) } reader.close() } ``` 2. 使用Kotlin的标准库函数:Kotlin的标准库提供了readText()和forEachLine()等函数,可以更简洁地读取文件内容。 ```kotlin import java.io.File fun main() { val file = File("path/to/file.txt") val content = file.readText() println(content) file.forEachLine { line -> println(line) } } ``` 3. 使用Kotlin的流式API:Kotlin的标准库还提供了流式API,可以方便地对文件内容进行流式处理。 ```kotlin import java.io.File fun main() { val file = File("path/to/file.txt") file.inputStream().bufferedReader().useLines { lines -> lines.forEach { line -> println(line) } } } ``` 无论使用哪种方式,都需要首先创建一个File对象来指定文件路径,并根据需要选择合适的读取方法来读取文件内容。读取完文件后,记得及时关闭相关的资源,以避免资源泄露。 ### 回答3: Kotlin读取文件的算法可以分为以下几个步骤: 1. 首先,需要使用Kotlin的File类来表示要读取的文件。可以通过提供文件的路径来创建一个File对象。 2. 然后,使用该File对象的readText()方法来读取文件的内容。readText()方法将文件的内容读取为一个字符串。 3. 如果要读取文件的内容行而不是整个文件的内容,可以使用readLines()方法来逐行读取文件。readLines()方法返回一个包含文件每一行内容的List对象。 4. 如果要处理大型文件,可以使用BufferedReader类来提高读取文件的性能。首先,通过File对象创建一个BufferedReader对象。然后,使用BufferedReader的readLine()方法逐行读取文件内容。 下面是一个示例代码,演示了如何使用Kotlin读取文件的算法: ```kotlin import java.io.File fun main() { val filePath = "path/to/file.txt" // 读取整个文件内容 val fileContent = File(filePath).readText() println(fileContent) // 逐行读取文件内容 val lines = File(filePath).readLines() for (line in lines) { println(line) } // 使用BufferedReader逐行读取文件内容 val bufferedReader = File(filePath).bufferedReader() var line: String? = bufferedReader.readLine() while (line != null) { println(line) line = bufferedReader.readLine() } } ``` 以上就是使用Kotlin读取文件的一种简单算法,可以根据需求调整读取方式以及处理文件内容的逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值