一、終端IO
println("text")
val line = readLine()
kotlin 擴(kuò)展了一下方法,使得在每個地方都可以直接,簡潔地調(diào)用到這些方法。
@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() 的實(shí)現(xiàn)。
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)
// 創(chuàng)建inputStream
public inline fun File.inputStream(): FileInputStream {
return FileInputStream(this)
}
// 創(chuàng)建inputStreamReader
public inline fun InputStream.reader(charset: Charset = Charsets.UTF_8): InputStreamReader = InputStreamReader(this, charset)
// 創(chuàng)建BufferedReader
public inline fun Reader.buffered(bufferSize: Int = DEFAULT_BUFFER_SIZE): BufferedReader
= if (this is BufferedReader) this else BufferedReader(this, bufferSize)
java的時候都是要一個流一個流地裝飾,現(xiàn)在kotlin擴(kuò)展了一些方法,使得我們可以簡單快捷地拿到裝飾好的流。
類似的流也有一些擴(kuò)展:
val bufferedInputStream = inputstream.buffered()
val bufferedReader = inputstream.bufferedReader()
val byteArrayInputStream = string.byteInputStream()
val inputStream = bytes.inputStream()
這些常見的轉(zhuǎn)換可以使我們很方便的使用。
2、通過file直接寫入文件
file.readBytes()
file.writeBytes(kotlin.ByteArray(1024))
file.readText()
file.writeText("")
可以通過file對象直接將數(shù)據(jù)寫入到文件中。
以readText為例子:
public fun File.readText(charset: Charset = Charsets.UTF_8): String = readBytes().toString(charset)
// 創(chuàng)建一個FileInputSream 并將數(shù)據(jù)讀取出來
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方法,這個方法可以幫處理關(guān)閉流的邏輯, 如果出現(xiàn)異常的話也會往上一層拋出
@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) }
// 創(chuàng)建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)
// 返回一個只能消費(fèi)一次的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 擴(kuò)展了一個FileTreeWalk.kt 的方法,其中主要擴(kuò)展了先根遍歷,和后根遍歷的方法,使得我們使用起來很方便。
其中還提供了onEnter,onLeave,onError方法,這些方法都是針對文件夾而言。
onEnter,第一次遍歷到該節(jié)點(diǎn),可以返回true代表可以繼續(xù)遍歷其子文件,返回false則直接跳過該文件夾
onLeave,當(dāng)遍歷完該文件中的所有子項(xiàng)的時候調(diào)用
onError,目前只有沒有權(quán)限訪問的錯誤
看看代碼:
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> {
這個對象實(shí)現(xiàn)了Sequence,所以其擁有filter,foreach方法
public fun File.walkTopDown(): FileTreeWalk = walk(FileWalkDirection.TOP_DOWN)
// 創(chuàng)建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)
}
// 該類中實(shí)現(xiàn)了iterator接口,然后這里實(shí)現(xiàn)了先根遍歷和后根遍歷的兩種算法
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擴(kuò)展了拷貝方法,從一個流寫到另一個流
public fun InputStream.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long
文件擴(kuò)展了copy方法,其中調(diào)用的是inputStream的copy方法
public fun File.copyTo(target: File, overwrite: Boolean = false, bufferSize: Int = DEFAULT_BUFFER_SIZE): File
文件擴(kuò)展了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 方法會遍歷所有文件,然后調(diào)用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
}
五、網(wǎng)絡(luò)IO
如果我們需要將一張圖片下載到本地的文件中的話可以通過下面簡短的代碼來實(shí)現(xiàn)
val imageUrl = "http://...."
File("path").writeBytes(URL(imageUrl).readBytes())
其中可以分為兩個部分,一個是網(wǎng)絡(luò)讀取,另外一個是文件寫入。
1、網(wǎng)絡(luò)讀取
URL(imageUrl).readBytes()
// 其實(shí)也就是調(diào)用了URL的openConnection,然后根據(jù)得到的connection拿到流,再讀取數(shù)據(jù)。
public fun URL.readBytes(): ByteArray = openStream().use { it.readBytes() }
public final InputStream openStream() throws java.io.IOException {
return openConnection().getInputStream();
}
這為我們節(jié)省了許多代碼量。
2、文件寫入
File("path").writeBytes()
這部分邏輯在上面已經(jīng)看到了,其就是將這個file包裝成為一個bufferWrite,然后寫入字節(jié)流
六、總結(jié)
上面大致列舉了一下kotlin io包中的一些擴(kuò)展函數(shù),以及這些擴(kuò)展函數(shù)給我們開發(fā)過程中帶來了很大的方便,更多好用的擴(kuò)展可以查看一下源碼或者是相關(guān)api。