在Kotlin中,如何将InputStream的全部内容读入String?

Jay*_*ard 85 inputstream kotlin

我最近看到了用于InputStream在Kotlin中读取字符串的全部内容的代码,例如:

// input is of type InputStream
val baos = ByteArrayOutputStream()
input.use { it.copyTo(baos) }
val inputAsString = baos.toString()
Run Code Online (Sandbox Code Playgroud)

并且:

val reader = BufferedReader(InputStreamReader(input))
try {
    val results = StringBuilder()
    while (true) { 
        val line = reader.readLine()
        if (line == null) break
        results.append(line) 
    }
    val inputAsString = results.toString()
} finally {
    reader.close()
}
Run Code Online (Sandbox Code Playgroud)

甚至这个看起来更顺畅,因为它会自动关闭InputStream:

val inputString = BufferedReader(InputStreamReader(input)).useLines { lines ->
    val results = StringBuilder()
    lines.forEach { results.append(it) }
    results.toString()
}
Run Code Online (Sandbox Code Playgroud)

或者那个略有变化:

val results = StringBuilder()
BufferedReader(InputStreamReader(input)).forEachLine { results.append(it) }
val resultsAsString = results.toString()   
Run Code Online (Sandbox Code Playgroud)

然后这个功能折叠东西:

val inputString = input.bufferedReader().useLines { lines ->
    lines.fold(StringBuilder()) { buff, line -> buff.append(line) }.toString()
}
Run Code Online (Sandbox Code Playgroud)

的变化,这不会关闭InputStream:

val inputString = BufferedReader(InputStreamReader(input))
        .lineSequence()
        .fold(StringBuilder()) { buff, line -> buff.append(line) }
        .toString()
Run Code Online (Sandbox Code Playgroud)

但它们都很笨重,而且我一直在寻找同样的新版本和不同版本...而其中一些版本甚至从未关闭过InputStream.什么是非笨重(惯用)的阅读方式InputStream

注意: 这个问题是由作者故意编写和回答的(答案问题),因此对于常见问题的Kotlin主题的惯用答案存在于SO中.

Jay*_*ard 184

Kotlin只是为了这个目的而有一个特定的扩展.

最简单的:

val inputAsString = input.bufferedReader().use { it.readText() }  // defaults to UTF-8
Run Code Online (Sandbox Code Playgroud)

在这个例子中,你可以决定bufferedReader()或只是reader().对函数的调用Closeable.use()将在lambda执行结束时自动关闭输入.

进一步阅读:

如果你做了很多类型的事情,你可以把它写成一个扩展函数:

fun InputStream.readTextAndClose(charset: Charset = Charsets.UTF_8): String {
    return this.bufferedReader(charset).use { it.readText() }
}
Run Code Online (Sandbox Code Playgroud)

然后您可以轻松调用:

val inputAsString = input.readTextAndClose()  // defaults to UTF-8
Run Code Online (Sandbox Code Playgroud)

在旁注中,所有需要知道charset已经默认的Kotlin扩展函数UTF-8,因此如果您需要不同的编码,则需要在调用中调整上面的代码以包含reader(charset)或的编码bufferedReader(charset).

警告:您可能会看到更短的示例:

val inputAsString = input.reader().readText() 
Run Code Online (Sandbox Code Playgroud)

但这些并没有关闭流.确保检查API文档中所用的所有IO函数,以确定哪些函数关闭,哪些不关闭.通常如果它们包括单词use(例如useLines()use())之后关闭流.一个例外是,File.readText()从不同Reader.readText()在于前者不留下任何东西打开,而后者确实需要一个明确的接近.

另请参阅: Kotlin IO相关的扩展功能

  • @ mfulton26我在这个例子中使用`readTextAndClose()`以避免与`readText()`不关闭的模式冲突,并且'use`模式需要lambda,因为我不想尝试引入新的stdlib函数我我们不想做更多关于使用扩展来节省未来劳动力的事情. (3认同)