Kotlin'de, bir InputStream'in tüm içeriğini bir String'e nasıl okuyabilirim?


107

Son zamanlarda InputStreambir Kotlin'de bir String'in tüm içeriğini okumak için kod gördüm , örneğin:

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

Ve ayrıca:

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

Ve otomatik olarak kapattığı için daha yumuşak görünen bu bile InputStream:

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

Veya bunda küçük bir değişiklik:

val results = StringBuilder()
BufferedReader(InputStreamReader(input)).forEachLine { results.append(it) }
val resultsAsString = results.toString()   

Sonra bu işlevsel katlama şeyi:

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

Veya şunu kapatmayan kötü bir varyasyon InputStream:

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

Ama hepsi hantal ve ben aynı şeyin daha yeni ve farklı versiyonlarını bulmaya devam ediyorum ... ve bazıları asla kapatmıyor bile InputStream. Okumanın hantal olmayan (deyimsel) bir yolu InputStreamnedir?

Not: Bu soru yazar tarafından bilinçli olarak yazılır ve yanıtlanır ( Kendi Kendine Cevaplanan Sorular ), böylece yaygın olarak sorulan Kotlin konularına verilen deyimsel cevaplar SO'da mevcuttur.

Yanıtlar:


220

Kotlin'in sadece bu amaç için belirli uzantıları vardır.

En basit:

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

Ve bu örnekte, arasında bufferedReader()veya sadece arasında karar verebilirsiniz reader(). İşleve yapılan çağrı Closeable.use(), lambda'nın çalıştırılmasının sonunda girişi otomatik olarak kapatacaktır.

Daha fazla okuma:

Bu tür şeyleri çok yaparsanız, bunu bir uzantı işlevi olarak yazabilirsiniz:

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

Daha sonra kolayca şu şekilde çağırabilirsiniz:

val inputAsString = input.readTextAndClose()  // defaults to UTF-8

Bir yan notta, charsetzaten varsayılanın bilinmesini gerektiren tüm Kotlin uzantı işlevleri UTF-8, bu nedenle farklı bir kodlamaya ihtiyaç duyuyorsanız, çağrılarda reader(charset)veya için bir kodlama eklemek için yukarıdaki kodu ayarlamanız gerekir bufferedReader(charset).

Uyarı: Daha kısa örnekler görebilirsiniz:

val inputAsString = input.reader().readText() 

Ancak bunlar akışı kapatmaz . Hangilerinin kapandığından ve hangilerinin kapanmadığından emin olmak için kullandığınız tüm GÇ işlevlerinin API belgelerini kontrol ettiğinizden emin olun. Genellikle use( useLines()veya gibi use()) kelimesini içeriyorlarsa akıntıyı sonra kapatın. Bunun bir istisnası olduğunu File.readText()gelen farklılık Reader.readText()eski açık bir şey bırakmaz ki ve ikincisi gerçekten açık bir kapanış gerektirir.

Ayrıca bkz: Kotlin IO ile ilgili uzantı işlevleri


1
Bence "readText", önerdiğiniz uzantı işlevi için "useText" den daha iyi bir isim olacaktır. "UseText" i okuduğumda, "kullanılan" şeye benzer useveya useLinesbir blok işlevi çalıştıran bir işlev bekliyorum . mesela inputStream.useText { text -> ... }ben "READTEXT" okurken, diğer yandan, metni döndüren bir işlev bekliyoruz: val inputAsString = inputStream.readText().
mfulton26

Doğru, ancak readText zaten yanlış anlama sahip, bu nedenle daha çok usebu bakımdan işlevlere benzediğini belirtmek istedim . en azından bu Soru-Cevap bağlamında. belki yeni bir fiil bulunabilir ...
Jayson Minard

3
@ mfulton26 readTextAndClose()Bu örnek için kapanmama readText()kalıplarıyla ve usebir lambda isteyen kalıplarla çelişmekten kaçınmak için gittim , çünkü yeni bir stdlib işlevi sunmaya çalışmadığım için kullanmakla ilgili bir noktadan daha fazlasını yapmak istemiyorum gelecekteki iş gücü tasarrufu için uzantılar.
Jayson Minard

@JaysonMinard neden bunu cevap için işaretlemiyorsun? yine de harika :-)
piotrek1543

2

Bir InputStream içeriğini bir String'e okuyan bir örnek

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

Referans İçin - Kotlin Dosyayı Oku


1
Örneğiniz kusurlar içeriyor: 1) Çapraz platform yolları için Paths.get()yöntemi kullanmalısınız . 2) Akışlar için - dene-kaynak özelliği (kotlin'de: .use {})
Evgeny Lebedev

0

Yöntem 1 | Akışı Manuel Olarak Kapatın

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
}

Yöntem 2 | Akışı Otomatik Olarak Kapat

private fun getFileText(uri: Uri): String {
    return contentResolver.openInputStream(uri)!!.bufferedReader().use {it.readText() }
}

cevabı iyileştirmek için lütfen bunun nasıl çalıştığını açıklayan bir açıklama yapın
Kirby
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.