53

So I've just started using Kotlin for Android, and converted my Android Java codes into Kotlin.

In one of the conversions, I stumbled upon a BufferedReader, which I would usually write in Java as the following:

String result = "";
String line = "";
BufferedReader reader = new BufferedReader(someStream);
while ( (line = reader.readLine()) != null ) {
    result += line;
}

But in Kotlin, it seems that Kotlin doesn't allow me to assign values to variables in while conditions.

Currently, I've written the code as the following:

val reader = BufferedReader(someStream)
var line : String? = ""
while (line != null) {
    line = reader.readLine()
    result += line
}

which I don't find so elegant and feels prev-gen, despite using Kotlin.

What would be the best way to use BufferedReader in Kotlin?

Akira Kido
  • 629
  • 1
  • 6
  • 11

9 Answers9

96

You can use bufferedReader like so

val allText = inputStream.bufferedReader().use(BufferedReader::readText)
Yoav Sternberg
  • 6,421
  • 3
  • 28
  • 30
miensol
  • 39,733
  • 7
  • 116
  • 112
  • 2
    An explanation: https://kotlinlang.org/docs/reference/idioms.html#java-7s-try-with-resources – voddan Dec 07 '16 at 12:27
24

If you still wanted to read it line by line you could use some extension functions from std lib and do it as follows:

val reader = someStream.bufferedReader()
val iterator = reader.linesSequences().iterator()
while(iterator.hasNext()) {
    val line = iterator.next()
    // do something with line...
}
reader.close()

or alternatively, using a "functional" approach:

val reader = someStream.bufferedReader()
reader.useLines {
    it.map { line -> // do something with line }
}

by using useLines, you don't need to explicitly call close on the reader, the useLines extensions function will do it for you!

Just adding those for reference.. cheers

  • Does `useLines` load all the lines at once? What if I am trying to read from a big file, will this use too much memory or is each line fetched as it is needed? Thanks in advance. – AurumTechie Jun 04 '20 at 04:53
  • 1
    So with useLines uses a sequence which uses a lazy evaluation over a stream which is good for open file handle count: (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-buffered-reader/line-sequence.html). Jetbrains suggests for large files File.forEachLine where you work with a single line at a time (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/for-each-line.html). But under the covers File.forEachLine still uses useLines with the same default buffer size of 8 * 1024 – baseman Jun 17 '20 at 03:51
22

you can also try using the "forEachLine" method.

val file = File("./folder/test.txt")
file.bufferedReader().forEachLine {
    println("value = $it")
} 

it'll also automatically close the stream after reading the last line

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-reader/index.html

fun Reader.forEachLine(action: (String) -> Unit)
Iterates through each line of this reader, calls action for each line read and closes the Reader when it's completed.

Angel Koh
  • 12,479
  • 7
  • 64
  • 91
5

Another way is to use a for loop:

val reader = BufferedReader(someStream)
for (line in reader.lines()) {
    println(line)
}

While is it not as concise as the accepted answer, it will allow you to loop thru and perform some kind of logic without pumping everything into one string as shown below

val allText: String = inputStream.bufferedReader().use(BufferedReader::readText)
Chris Ritchie
  • 4,749
  • 2
  • 36
  • 33
  • 2
    Please note: You can iterate over lines using `useLines()` method which returns a lazy Sequence and closes the underlying BufferedReader afterwards for you. – blahblah Feb 20 '19 at 11:15
2

Thanks to João Gonçalves reference to the stdlib I found that you can use forEachLine to traverse the reader if needed.

Alejandro Moya
  • 434
  • 6
  • 11
1

use the code like this


    val input = conn.inputStream
    val allText = input.bufferedReader().use(BufferedReader::readText)
    val result = StringBuilder()                   

    result.append(allText)
    return result.toString()

    } else {

    return "unsuccessful"

    }

reza rahmad
  • 1,009
  • 10
  • 16
1

The accepted answer fails when the stream has multiple lines. This is my solution

val allText = inputStream.bufferedReader().use { it.readLines().joinToString("") }
unify
  • 6,161
  • 4
  • 33
  • 34
1

You can use BufferReader like this :

val data = inputStream.bufferedReader().use(BufferedReader::readText)
    
Nikhil Katekhaye
  • 2,344
  • 1
  • 18
  • 19
0

This solution includes the character \n after each line

val fileInputStream = context.openFileInput(fileName)
val reader = BufferedReader(InputStreamReader(fileInputStream))
val line = reader.buffered().use { it.readText() }