Як у Котліні я читаю весь вміст InputStream у String?


105

Нещодавно я побачив код для читання всього вмісту InputStreamв 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?

Примітка: це запитання навмисно написано та відповіло автором ( запитання з самовідповіддю ), так що ідіоматичні відповіді на поширені теми Котліна присутні в ТА.

Відповіді:


216

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

Зі сторони, всі функції розширення Kotlin, які вимагають знати charsetвже за замовчуванням UTF-8, тому, якщо вам потрібне інше кодування, вам потрібно відкоригувати код вище у викликах, щоб включити кодування для reader(charset)або bufferedReader(charset).

Попередження: Ви можете побачити короткі приклади:

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

Але ці не закривають потік . Переконайтеся, що ви перевірте документацію API для всіх функцій вводу-виводу, якими ви користуєтесь, щоб бути впевненими, які з них закриваються, а які - ні. Зазвичай, якщо вони включають слово use(наприклад, useLines()або use()), закрийте потік після. Виняток є тим, що File.readText()відрізняється Reader.readText()тим, що перший не залишає нічого відкритим, а другий дійсно вимагає явного закриття.

Дивіться також: Котлін IO, пов'язані функції розширення


1
Я думаю, що "readText" буде кращою назвою, ніж "useText" для запропонованої вами функції розширення. Коли я читаю "useText", я очікую, що функція на кшталт useабо useLinesяка виконує функцію блоку щодо того, що "використовується". наприклад , inputStream.useText { text -> ... }З іншого боку, коли я прочитав «READTEXT» Я очікую , що функція , яка повертає текст: val inputAsString = inputStream.readText().
mfulton26

Щоправда, але readText вже має неправильне значення, тому хотілося означити, що це більше нагадує useфункції в цьому плані. принаймні в контексті цього питання. може бути, знайдеться нове дієслово ...
Jayson Minard

3
@ mfulton26 Я пішов з readTextAndClose()цим прикладом, щоб уникнути конфлікту з readText()моделями не закриття та з useшаблонами, які хочуть лямбда, оскільки я не намагаюся запровадити нову функцію stdlib, я не хочу робити більше, ніж задуматися про використання розширення для економії майбутньої праці.
Джейсон Мінард

@JaysonMinard чому ти не позначиш це для відповіді? чудово, хоча :-)
piotrek1543

2

Приклад, який читає вміст InputStream до 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)
}

Довідково - Котлін Прочитаний файл


1
Ваш приклад містить недоліки: 1) Для міжплатформних шляхів слід використовувати Paths.get()метод. 2) Для потоків - спробуйте-ресурс (In kotlin: .use {})
Євген Лебедєв
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.