Kotlinで、InputStreamのコンテンツ全体を文字列に読み込むにはどうすればよいですか?


105

私は最近InputStream、KotlinのStringにの内容全体を読み取るためのコードを見ました。

// 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何ですか?

注: この質問は作者が意図的に作成して回答したものであり(自己回答式の質問)、一般的に尋ねられるKotlinトピックに対する慣用的な回答がSOにあります。

回答:


215

Kotlinには、この目的のためだけに特定の拡張機能があります。

最も簡単な:

val inputAsString = input.bufferedReader().use { it.readText() }  // defaults to UTF-8

そして、この例では、どちらにするか、bufferedReader()または単に決めることができますreader()。関数の呼び出しCloseable.use()は、ラムダの実行の最後に入力を自動的に閉じます。

参考文献:

このタイプのことをたくさん行う場合、これを拡張関数として書くことができます:

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() 

しかし、これらはストリームを閉じません。使用しているすべてのIO関数APIドキュメントを確認して、どれが閉じてどれが閉じていないかを確認してください。通常、それらにuseuseLines()またはuse())などの単語が含まれている場合は、後でストリームを閉じます。例外は、前者は何も開いたままにせず、後者は確かに明示的な閉じを必要とするというFile.readText()点で異なりReader.readText()ます。

参照: Kotlin IO関連の拡張関数


1
私が提案する拡張関数では、「readText」の方が「useText」よりもいい名前だと思います。私は「useText」を読んだときのように、私は機能を期待するuseか、useLines「使用」されているもののブロック機能を実行します。たとえばinputStream.useText { text -> ... }、一方で、「readText」を読んだ場合、テキストを返す関数が必要ですval inputAsString = inputStream.readText()
mfulton26 2016

確かに、しかしreadTextはすでに間違った意味を持っているのでuse、その点でそれが関数のようであることを示したいと思いました。少なくともこのQ&Aに関しては。新しい動詞が見つかるかもしれません...
Jayson Minard

3
@ mfulton26 readTextAndClose()この例readText()では、閉じないuseパターンとラムダを必要とするパターンとの競合を回避するために使用しました。新しいstdlib関数を導入するつもりはないので、使用することを強調する以上のことはしたくありません。将来の労力を節約する拡張機能。
Jayson Minard

@JaysonMinardなぜこれに答えのマークを付けませんか?それも素晴らしいです:-)
piotrek1543

2

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読み取りファイル


あなたの例は欠陥を含んでいます:1)クロスプラットフォームパスのために、あなたはPaths.get()メソッドを使うべきです。2)ストリームの場合-try-resource機能(kotlinの場合:.use {})
Evgeny Lebedev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.