No Kotlin, como leio todo o conteúdo de um InputStream em uma String?


105

Recentemente, vi um código para ler todo o conteúdo de um InputStreamem uma String no Kotlin, como:

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

E também:

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

E até mesmo isso parece mais suave, pois fecha automaticamente InputStream:

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

Ou ligeira variação desse:

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

Então, esta dobra funcional:

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

Ou uma variação ruim que não fecha o InputStream:

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

Mas eles são todos desajeitados e eu continuo encontrando versões mais novas e diferentes do mesmo ... e alguns deles nunca fecham o InputStream. O que é uma maneira não desajeitada (idiomática) de ler o InputStream?

Observação: esta pergunta foi escrita e respondida intencionalmente pelo autor ( Perguntas auto-respondidas ), de modo que as respostas idiomáticas aos tópicos mais comuns do Kotlin estejam presentes no SO.

Respostas:


216

O Kotlin possui extensões específicas apenas para este propósito.

O mais simples:

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

E neste exemplo você pode decidir entre bufferedReader()ou apenas reader(). A chamada para a função Closeable.use()fechará automaticamente a entrada no final da execução do lambda.

Leitura adicional:

Se você faz muito esse tipo de coisa, pode escrever como uma função de extensão:

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

Que você pode chamar facilmente de:

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

Em uma observação lateral, todas as funções de extensão Kotlin que exigem o conhecimento do charsetjá padrão UTF-8, portanto, se você precisar de uma codificação diferente, precisará ajustar o código acima nas chamadas para incluir uma codificação para reader(charset)ou bufferedReader(charset).

Aviso: você pode ver exemplos mais curtos:

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

Mas isso não fecha o riacho . Certifique-se de verificar a documentação da API para todas as funções IO que você usa para ter certeza de quais fecham e quais não. Normalmente, se eles incluem a palavra use(como useLines()ou use()), feche o riacho depois. Uma exceção é que File.readText()difere de Reader.readText()que o primeiro não deixa nada em aberto e o último realmente requer um fechamento explícito.

Consulte também: Funções de extensão relacionadas ao Kotlin IO


1
Acho que "readText" seria um nome melhor do que "useText" para a função de extensão que você propõe. Quando leio "useText", espero uma função como useou useLinesque executa uma função de bloco sobre o que está sendo "usado". eg inputStream.useText { text -> ... }Por outro lado, quando eu li "ReadText" Espero uma função que retorna o texto: val inputAsString = inputStream.readText().
mfulton26 de

Verdade, mas readText já tem o significado errado, então queria significar que era mais parecido com as usefunções a esse respeito. pelo menos no contexto deste Q&A. talvez um novo verbo possa ser encontrado ...
Jayson Minard

3
@ mfulton26 Usei readTextAndClose()este exemplo para evitar conflito com readText()padrões de não fechamento e com usepadrões que desejam um lambda, já que não estou tentando introduzir uma nova função stdlib, não quero fazer mais do que enfatizar o uso extensões para economizar trabalho futuro.
Jayson Minard

@JaysonMinard, por que você não marca isso como resposta? é ótimo :-)
piotrek1543

2

Um exemplo que lê o conteúdo de um InputStream para uma String

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

Para referência - arquivo de leitura Kotlin


Seu exemplo contém falhas: 1) Para caminhos de plataforma cruzada, você deve usar o Paths.get()método. 2) Para streams - recurso try-resource (em kotlin: .use {})
Evgeny Lebedev
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.