Trong Kotlin, làm cách nào để đọc toàn bộ nội dung của InputStream thành một chuỗi?


105

Gần đây tôi đã thấy mã để đọc toàn bộ nội dung của InputStreammột chuỗi trong Kotlin, chẳng hạn như:

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

Và ngoài ra:

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

Và thậm chí điều này trông mượt mà hơn vì nó tự động đóng InputStream:

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

Hoặc biến thể nhỏ trên cái đó:

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

Sau đó, điều này gấp chức năng:

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

Hoặc một biến thể xấu không đóng InputStream:

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

Nhưng tất cả chúng đều rất khó hiểu và tôi tiếp tục tìm các phiên bản mới hơn và khác nhau của cùng một phiên bản ... và một số trong số chúng thậm chí không bao giờ đóng lại InputStream. Cách đọc không rườm rà (thành ngữ) là InputStreamgì?

Lưu ý: câu hỏi này được tác giả viết và trả lời có chủ ý ( Câu hỏi tự trả lời ), vì vậy các câu trả lời thành ngữ cho các chủ đề Kotlin thường được hỏi đều có trong SO.

Câu trả lời:


216

Kotlin có một phần mở rộng cụ thể chỉ dành cho mục đích này.

Điều đơn giản nhất:

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

Và trong ví dụ này, bạn có thể quyết định giữa bufferedReader()hoặc chỉ reader(). Lời gọi đến hàm Closeable.use()sẽ tự động đóng đầu vào khi kết thúc quá trình thực thi của lambda.

Đọc thêm:

Nếu bạn làm kiểu này nhiều, bạn có thể viết nó như một hàm mở rộng:

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

Sau đó, bạn có thể dễ dàng gọi là:

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

Lưu ý nhỏ, tất cả các chức năng của tiện ích mở rộng Kotlin đều yêu cầu biết giá trị charsetđã được mặc định sẵn UTF-8, vì vậy nếu bạn yêu cầu một mã hóa khác, bạn cần điều chỉnh mã ở trên trong các cuộc gọi để bao gồm mã hóa cho reader(charset)hoặc bufferedReader(charset).

Cảnh báo: Bạn có thể thấy các ví dụ ngắn hơn:

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

Nhưng những điều này không đóng dòng . Đảm bảo rằng bạn kiểm tra tài liệu API cho tất cả các chức năng IO mà bạn sử dụng để biết chắc cái nào đóng và cái nào không. Thông thường, nếu chúng bao gồm từ use(chẳng hạn như useLines()hoặc use()) bạn đóng luồng sau. Một ngoại lệ là File.readText()khác Reader.readText()ở chỗ cái trước không để ngỏ gì và cái sau thực sự yêu cầu đóng một cách rõ ràng.

Xem thêm: Các chức năng mở rộng liên quan đến Kotlin IO


1
Tôi nghĩ rằng "readText" sẽ là một cái tên tốt hơn "useText" cho chức năng mở rộng mà bạn đề xuất. Khi tôi đọc "useText", tôi mong đợi một hàm giống như usehoặc useLinesthực thi một hàm khối trên những gì đang được "sử dụng". ví dụ như inputStream.useText { text -> ... }Mặt khác, khi tôi đọc "readText" Tôi mong đợi một chức năng mà trả về văn bản: val inputAsString = inputStream.readText().
mfulton26

Đúng, nhưng readText đã có nghĩa sai, vì vậy muốn biểu thị nó giống với các usehàm về mặt đó hơn. ít nhất là trong ngữ cảnh của Hỏi và Đáp này. có thể là một động từ mới có thể được tìm thấy ...
Jayson Minard

3
@ mfulton26 Tôi đã sử readTextAndClose()dụng ví dụ này để tránh xung đột với readText()các mẫu không đóng và với usecác mẫu muốn có lambda, vì tôi không cố gắng giới thiệu một hàm stdlib mới nên tôi không muốn làm gì hơn ngoài việc nói rõ về việc sử dụng phần mở rộng để tiết kiệm sức lao động trong tương lai.
Jayson Minard

@JaysonMinard tại sao bạn không đánh dấu điều này để trả lời? đó là mặc dù lớn :-)
piotrek1543

2

Một ví dụ đọc nội dung của Dòng đầu vào thành Chuỗi

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

Để tham khảo - Kotlin Đọc tệp


Ví dụ của bạn chứa các sai sót: 1) Đối với các đường dẫn đa nền tảng, bạn nên sử dụng Paths.get()phương pháp. 2) Đối với suối - Tính năng thử tài nguyên (Trong Kotlin: .use {})
Evgeny Lebedev
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.