113

I have a handy function that I've used in Java for converting an InputStream to a String. Here is a direct translation to Scala:

  def inputStreamToString(is: InputStream) = {
    val rd: BufferedReader = new BufferedReader(new InputStreamReader(is, "UTF-8")) 
    val builder = new StringBuilder()    
    try {
      var line = rd.readLine 
      while (line != null) { 
        builder.append(line + "\n")
        line = rd.readLine
      }
    } finally {
      rd.close
    }
    builder.toString
  }

Is there an idiomatic way to do this in scala?

bluish
  • 23,093
  • 23
  • 110
  • 171
bballant
  • 1,375
  • 2
  • 9
  • 8

3 Answers3

200

For Scala >= 2.11

scala.io.Source.fromInputStream(is).mkString

For Scala < 2.11:

scala.io.Source.fromInputStream(is).getLines().mkString("\n")

does pretty much the same thing. Not sure why you want to get lines and then glue them all back together, though. If you can assume the stream's nonblocking, you could just use .available, read the whole thing into a byte array, and create a string from that directly.

Flow
  • 22,048
  • 13
  • 91
  • 147
Rex Kerr
  • 162,454
  • 26
  • 308
  • 402
  • 2
    One possible reason, that I've used myself, is to normalize line endings on different operating systems. – Kevin Wright Mar 07 '11 at 17:42
  • Raam's answer is also awesome (and slightly more concise), but marking Rex's as THE answer, because it's more specifically like the example. Glueing the lines back together was specific a few cases, but you reminded me that I've used this code in places where it isn't quite appropriate to do that. – bballant Mar 07 '11 at 18:40
  • the solution is not very safe as it uses getLines(); what if the input stream has no "new line" characters? then the whole thing blocks – Paul Sabou Mar 17 '13 at 12:11
  • Quite a bad solution. What if the inputstream contains DOS line-endings (\r\n). These would be removed by this method. Also, although mkString uses a buffer, it most certainly would be faster to read blocks of characters. – Dibbeke Jun 23 '14 at 11:42
  • @Dibbeke - It's the same output the OP said they wanted, even if it's not completely faithful. Anyway, there's a better option now, which I've added. – Rex Kerr Jun 23 '14 at 19:44
  • 1
    @RexKerr Can you please point out the "performance bug" that you mentioned in your answer. I tested both versions with some basic testcases and didn't hit any issue. – Sahil Sareen Sep 15 '15 at 16:11
74

Source.fromInputStream(is).mkString("") will also do the deed.....

raam
  • 741
  • 4
  • 2
  • Good point; source creates something that extends `Iterator[Char]`. – Rex Kerr Mar 07 '11 at 16:59
  • 8
    It's generally good practice to also specify the character encoding when doing this sort of thing. To that end: `Source.fromInputStream(is)(Codec.UTF8).mkString` – Connor Doyle Aug 20 '13 at 20:42
  • This is terse, but it doesn't close the stream, whereas the original java code did. – Rich Jan 28 '14 at 13:36
  • @Rich, `fromInputStream()` appears to close the stream, at least in Scala 2.11. – jaco0646 Dec 03 '16 at 22:44
  • 2
    @jaco0646 -- it does not close the stream. I just tested. Here is demo code that proves it: https://gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c – Rich Dec 08 '16 at 13:09
  • @Rich, you're right. I looked again at the method implementation. It is wrapping the `close()` method of the `InputStream` within the `close()` method of the `BufferedSource` so that closing the source closes the stream. It is still the responsibility of the client to close the source. – jaco0646 Dec 08 '16 at 19:30
14

Faster way to do this:

    private def inputStreamToString(is: InputStream) = {
        val inputStreamReader = new InputStreamReader(is)
        val bufferedReader = new BufferedReader(inputStreamReader)
        Iterator continually bufferedReader.readLine takeWhile (_ != null) mkString
    }
Kamil Lelonek
  • 13,631
  • 10
  • 60
  • 87
  • "faster"? But it provided me the answer for how to do it when I just have a `Reader` and not an `InputStream`. – BeepDog Jan 30 '14 at 22:12
  • 3
    Just skip the first line, and pass `inputStreamReader` to method. – Kamil Lelonek Jan 31 '14 at 07:20
  • 1
    This is potentially an order of magnitude faster than scala.io.Source in Scala 2.11.7. I wrote a really basic benchmark of it and most of the time, it was about 5% faster for large files (test was ~35 MB text file) all the way up to 2,800% faster for small files (test was ~30 KB). – Colin Dean Nov 06 '15 at 19:31
  • 2
    Beautiful. Been struggling for an elegant solution reading large inputs from `Runtime.exec()`. This nails it. – Pavel Lechev Dec 05 '16 at 13:19
  • How would I specify a character set to use? – wheeler Oct 13 '17 at 14:17
  • Years later, I found a bug in this: it trashes the line ending markers, `\n`, `\r`, or `\r\n`. – Colin Dean Oct 24 '18 at 20:35