在 Kotlin 中,如何将 InputStream 的全部内容读入字符串?
Posted
技术标签:
【中文标题】在 Kotlin 中,如何将 InputStream 的全部内容读入字符串?【英文标题】:In Kotlin, how do I read the entire contents of an InputStream into a String? 【发布时间】:2017-01-22 19:50:37 【问题描述】:我最近在 Kotlin 中看到了将 InputStream
的全部内容读入字符串的代码,例如:
// input is of type InputStream
val baos = ByteArrayOutputStream()
input.use it.copyTo(baos)
val inputAsString = baos.toString()
还有:
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()
即使这个看起来更流畅,因为它会自动关闭InputStream
:
val inputString = BufferedReader(InputStreamReader(input)).useLines lines ->
val results = StringBuilder()
lines.forEach results.append(it)
results.toString()
或者在那个上略有不同:
val results = StringBuilder()
BufferedReader(InputStreamReader(input)).forEachLine results.append(it)
val resultsAsString = results.toString()
然后这个功能折叠的东西:
val inputString = input.bufferedReader().useLines lines ->
lines.fold(StringBuilder()) buff, line -> buff.append(line) .toString()
或者没有关闭InputStream
的坏变体:
val inputString = BufferedReader(InputStreamReader(input))
.lineSequence()
.fold(StringBuilder()) buff, line -> buff.append(line)
.toString()
但是它们都很笨重,我一直在寻找相同的更新和不同版本......其中一些甚至从未关闭InputStream
。阅读InputStream
的非笨拙(惯用)方式是什么?
注意: 此问题由作者 (Self-Answered Questions) 有意编写和回答,因此常见的 Kotlin 主题的惯用答案出现在 SO 中。
【问题讨论】:
【参考方案1】:Kotlin 有一个专门用于此目的的扩展。
最简单的:
val inputAsString = input.bufferedReader().use it.readText() // defaults to UTF-8
在此示例中,您可以在bufferedReader()
或仅reader()
之间做出选择。对函数 Closeable.use()
的调用将在 lambda 执行结束时自动关闭输入。
进一步阅读:
如果你经常做这种事情,你可以把它写成一个扩展函数:
fun InputStream.readTextAndClose(charset: Charset = Charsets.UTF_8): String
return this.bufferedReader(charset).use it.readText()
然后您可以轻松调用:
val inputAsString = input.readTextAndClose() // defaults to UTF-8
附带说明,所有需要知道 charset
的 Kotlin 扩展函数已经默认为 UTF-8
,因此如果您需要不同的编码,您需要在调用中调整上面的代码以包含 reader(charset)
的编码或bufferedReader(charset)
.
警告:您可能会看到更短的示例:
val inputAsString = input.reader().readText()
但是这些不会关闭流。确保检查您使用的API documentation for all of the IO functions 以确定哪些关闭,哪些不关闭。通常,如果它们包含单词use
(例如useLines()
或use()
),它们会在之后关闭流。一个例外是 File.readText()
与 Reader.readText()
的不同之处在于前者不会打开任何东西,而后者确实需要显式关闭。
另请参阅: Kotlin IO related extension functions
【讨论】:
对于您提出的扩展功能,我认为“readText”会比“useText”更好。当我阅读“useText”时,我希望有一个像use
或useLines
这样的函数,它对正在“使用”的内容执行一个块函数。例如inputStream.useText text -> ...
另一方面,当我阅读“readText”时,我希望有一个返回文本的函数:val inputAsString = inputStream.readText()
。
是的,但是 readText 已经有错误的含义,所以想表明它在这方面更像use
函数。至少在本问答的背景下。也许可以找到一个新的动词......
@mfulton26 我在这个例子中选择了readTextAndClose()
以避免与readText()
不关闭的模式和use
想要一个 lambda 的模式发生冲突,因为我不想引入一个新的 stdlib 函数我不想做的只是说明使用扩展来节省未来的劳动力。
@JaysonMinard 为什么不将此标记为答案?不过很好:-)
自 Kotlin 1.3 以来,readBytes
+ String 构造函数可以说更简单,但它仅限于 2GB,因此可能不值得推荐(因此此评论,而不是答案):inputStream.use String(it.readBytes())
。默认为 UTF-8,但您也可以将编码传递给 String
构造函数。尽管如此,如果阅读文本,使用InputStreamReader
似乎是一个好习惯,即使小于 2GB,这可能是大部分时间。【参考方案2】:
【方法一 |手动关闭流】
private fun getFileText(uri: Uri):String
val inputStream = contentResolver.openInputStream(uri)!!
val bytes = inputStream.readBytes() //see below
val text = String(bytes, StandardCharsets.UTF_8) //specify charset
inputStream.close()
return text
inputStream.readBytes()
需要手动关闭流:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-input-stream/read-bytes.html
【方法二 |自动关闭流】
private fun getFileText(uri: Uri): String
return contentResolver.openInputStream(uri)!!.bufferedReader().use it.readText()
你可以指定bufferedReader()
里面的字符集,默认是UTF-8
:
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-input-stream/buffered-reader.html
bufferedReader()
是reader()
的升级版,用途更广:
How exactly does bufferedReader() work in Kotlin?
use()
可以在块完成时自动关闭流:
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html
【讨论】:
为了改进答案,请给出一些叙述来解释这是如何工作的 你为什么在你的例子之间混合readBytes
和readText
?自动关闭流由use
处理,BufferedReader
/readText
和readBytes
的选择与此无关。第二个示例也与已接受答案顶部的示例相同。【参考方案3】:
将 InputStream 的内容读取到字符串的示例
import java.io.File
import java.io.InputStream
import java.nio.charset.Charset
fun main(args: Array<String>)
val file = File("input"+File.separator+"contents.txt")
var ins:InputStream = file.inputStream()
var content = ins.readBytes().toString(Charset.defaultCharset())
println(content)
供参考 - Kotlin Read File
【讨论】:
您的示例包含缺陷:1) 对于跨平台路径,您应该使用Paths.get()
方法。 2) 对于流 - try-resource 功能(在 kotlin 中:.use )【参考方案4】:
快速解决方案在将 InputStream 转换为字符串时效果很好。
val convertedInputStream = String(inputStream.readAllBytes(), StandardCharsets.UTF_8)
【讨论】:
InputStream
上没有 readAllBytes
方法。也许你的意思是readBytes
?如果是这样,那么您还需要关闭流。以上是关于在 Kotlin 中,如何将 InputStream 的全部内容读入字符串?的主要内容,如果未能解决你的问题,请参考以下文章
如何读取将作为 MultipartFile 参数传递的 SQLite 文件? (在 java/kotlin 中)
如何将 Kotlin 错误链接到 Android Studio 中的源?