Kotlin IO 新特性

一、终端IO

println("text")
val line = readLine()

kotlin 扩展了一下方法,使得在每个地方都可以直接,简洁地调用到这些方法。

@kotlin.internal.InlineOnly
public inline fun println(message: Any?) {
    System.out.println(message)
}
public fun readLine(): String? = stdin.readLine()

stdin 是一个BufferedReader,其修饰了System.in 然后使用BufferedReader的readLine方法

二、文件IO

1、可以通过file直接拿到流

file.inputStream()
file.outputStream()

file.bufferedReader()
file.bufferedWriter()

可以大概看一下其中的bufferedReader() 的实现。

public inline fun File.bufferedReader(charset: Charset = Charsets.UTF_8, bufferSize: Int = DEFAULT_BUFFER_SIZE): BufferedReader = reader(charset).buffered(bufferSize)

public inline fun File.reader(charset: Charset = Charsets.UTF_8): InputStreamReader = inputStream().reader(charset)

// 创建inputStream
public inline fun File.inputStream(): FileInputStream {
    return FileInputStream(this)
}

// 创建inputStreamReader
public inline fun InputStream.reader(charset: Charset = Charsets.UTF_8): InputStreamReader = InputStreamReader(this, charset)
    
// 创建BufferedReader
public inline fun Reader.buffered(bufferSize: Int = DEFAULT_BUFFER_SIZE): BufferedReader
        = if (this is BufferedReader) this else BufferedReader(this, bufferSize)

java的时候都是要一个流一个流地装饰,现在kotlin扩展了一些方法,使得我们可以简单快捷地拿到装饰好的流。
类似的流也有一些扩展:

val bufferedInputStream = inputstream.buffered()
val bufferedReader = inputstream.bufferedReader()
val byteArrayInputStream = string.byteInputStream()
val inputStream = bytes.inputStream()

这些常见的转换可以使我们很方便的使用。

2、通过file直接写入文件

file.readBytes()
file.writeBytes(kotlin.ByteArray(1024))

file.readText()
file.writeText("")

可以通过file对象直接将数据写入到文件中。
以readText为例子:

public fun File.readText(charset: Charset = Charsets.UTF_8): String = readBytes().toString(charset)
// 创建一个FileInputSream 并将数据读取出来
public fun File.readBytes(): ByteArray = FileInputStream(this).use { input ->
    var offset = 0
    var remaining = this.length().let {
        if (it > Int.MAX_VALUE) throw OutOfMemoryError("File $this is too big ($it bytes) to fit in memory.") else it
    }.toInt()
    val result = ByteArray(remaining)
    while (remaining > 0) {
        val read = input.read(result, offset, remaining)
        if (read < 0) break
        remaining -= read
        offset += read
    }
    if (remaining == 0) result else result.copyOf(offset)
}

// 上面使用到了一个use方法,这个方法可以帮处理关闭流的逻辑,  如果出现异常的话也会往上一层抛出
@InlineOnly
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    var closed = false
    try {
        return block(this)
    } catch (e: Exception) {
        closed = true
        try {
            this?.close()
        } catch (closeException: Exception) {
        }
        throw e
    } finally {
        if (!closed) {
            this?.close()
        }
    }
}

另外还有append方法

file.appendText("sss", UTF_8)
file.appendBytes(kotlin.ByteArray(1024))

3、遍历获取行

file.forEachLine { it }
file.useLines { lines -> {} }
public fun File.forEachLine(charset: Charset = Charsets.UTF_8, action: (line: String) -> Unit): Unit {
    // Note: close is called at forEachLine
    BufferedReader(InputStreamReader(FileInputStream(this), charset)).forEachLine(action)
}
// 遍历BufferedReader, 读取到的每一行
public fun Reader.forEachLine(action: (String) -> Unit): Unit = useLines { it.forEach(action) }

// 创建BufferedReader, 读取所有行
public inline fun <T> Reader.useLines(block: (Sequence<String>) -> T): T =
        buffered().use { block(it.lineSequence()) }

public inline fun Reader.buffered(bufferSize: Int = DEFAULT_BUFFER_SIZE): BufferedReader
        = if (this is BufferedReader) this else BufferedReader(this, bufferSize)
// 返回一个只能消费一次的Sequence
public fun <T> Sequence<T>.constrainOnce(): Sequence<T> {
    return if (this is ConstrainedOnceSequence<T>) this else ConstrainedOnceSequence(this)
}

@kotlin.jvm.JvmVersion
private class ConstrainedOnceSequence<T>(sequence: Sequence<T>) : Sequence<T> {
    private val sequenceRef = java.util.concurrent.atomic.AtomicReference(sequence)

    override fun iterator(): Iterator<T> {
        val sequence = sequenceRef.getAndSet(null) ?: throw IllegalStateException("This sequence can be consumed only once.")
        return sequence.iterator()
    }
}

三、文件遍历

file.walk()
    .maxDepth(3)
    .onEnter {
        it.name != "break"
    }.onLeave {  }
    .onFail({ _: File, _: IOException -> {}})
    .filter { it.isFile }
    .forEach {
        println(it.name)
    }
    file.walkTopDown()
    file.walkBottomUp()

kotlin 扩展了一个FileTreeWalk.kt 的方法,其中主要扩展了先根遍历,和后根遍历的方法,使得我们使用起来很方便。
其中还提供了onEnter,onLeave,onError方法,这些方法都是针对文件夹而言。
onEnter,第一次遍历到该节点,可以返回true代表可以继续遍历其子文件,返回false则直接跳过该文件夹
onLeave,当遍历完该文件中的所有子项的时候调用
onError,目前只有没有权限访问的错误

看看代码:

public class FileTreeWalk private constructor(
        private val start: File,
        private val direction: FileWalkDirection = FileWalkDirection.TOP_DOWN,
        private val onEnter: ((File) -> Boolean)?,
        private val onLeave: ((File) -> Unit)?,
        private val onFail: ((f: File, e: IOException) -> Unit)?,
        private val maxDepth: Int = Int.MAX_VALUE
) : Sequence<File> {

这个对象实现了Sequence,所以其拥有filter,foreach方法

public fun File.walkTopDown(): FileTreeWalk = walk(FileWalkDirection.TOP_DOWN)
    
// 创建FileTreeWalk对象
public fun File.walk(direction: FileWalkDirection = FileWalkDirection.TOP_DOWN): FileTreeWalk =
        FileTreeWalk(this, direction)
    
// 修改并返回新对象, 其中onEnter,onLeave,onFail是一样的。
public fun maxDepth(depth: Int): FileTreeWalk {
        if (depth <= 0)
            throw IllegalArgumentException("depth must be positive, but was $depth.")
        return FileTreeWalk(start, direction, onEnter, onLeave, onFail, depth)
    }

// 该类中实现了iterator接口,然后这里实现了先根遍历和后根遍历的两种算法
private abstract class WalkState(val root: File) {
        /** Call of this function proceeds to a next file for visiting and returns it */
        abstract public fun step(): File?
    }
private inner class TopDownDirectoryState(rootDir: File)
private inner class BottomUpDirectoryState(rootDir: File) 

四、文件拷贝,删除

1、拷贝

inputStream扩展了拷贝方法,从一个流写到另一个流

public fun InputStream.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long 

文件扩展了copy方法,其中调用的是inputStream的copy方法

public fun File.copyTo(target: File, overwrite: Boolean = false, bufferSize: Int = DEFAULT_BUFFER_SIZE): File 

文件扩展了copyRecursicely方法,支持拷贝文件夹,其中用到了文件遍历,以及File.copyTo方法

public fun File.copyRecursively(target: File,
                                overwrite: Boolean = false,
                                onError: (File, IOException) -> OnErrorAction =
                                         { _, exception -> throw exception }
): Boolean 

2、文件夹以及子文件的删除操作

public fun File.deleteRecursively(): Boolean = walkBottomUp().fold(true, { res, it -> (it.delete() || !it.exists()) && res })

其中fold 方法会遍历所有文件,然后调用operation方法。 删除操作的时候初始值为true,如果中间有一个删除失败的情况的话则最终返回值为false

public inline fun <T, R> Sequence<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
    var accumulator = initial
    for (element in this) accumulator = operation(accumulator, element)
    return accumulator
}

五、网络IO

如果我们需要将一张图片下载到本地的文件中的话可以通过下面简短的代码来实现

val imageUrl = "http://...."
File("path").writeBytes(URL(imageUrl).readBytes())

其中可以分为两个部分,一个是网络读取,另外一个是文件写入。

1、网络读取

URL(imageUrl).readBytes()
// 其实也就是调用了URL的openConnection,然后根据得到的connection拿到流,再读取数据。
public fun URL.readBytes(): ByteArray = openStream().use { it.readBytes() }

public final InputStream openStream() throws java.io.IOException {
        return openConnection().getInputStream();
    }

这为我们节省了许多代码量。

2、文件写入

File("path").writeBytes()

这部分逻辑在上面已经看到了,其就是将这个file包装成为一个bufferWrite,然后写入字节流

六、总结

上面大致列举了一下kotlin io包中的一些扩展函数,以及这些扩展函数给我们开发过程中带来了很大的方便,更多好用的扩展可以查看一下源码或者是相关api。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,812评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,626评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,144评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,052评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,925评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,035评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,461评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,150评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,413评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,501评论 2 307
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,277评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,159评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,528评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,868评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,143评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,407评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,615评论 2 335

推荐阅读更多精彩内容