51

In many examples, it is described that you can use scala.io.Source to read a whole file like this:

val str = scala.io.Source.fromFile("test.txt").mkString()

But closing the underlying stream is not mentioned.

Why does Scala not provide a convenient way to do that such as with clause in Python? It looks useful but not difficult.

Is there any other better way to do that safely in Scala, I means to read a whole file?

Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
woods
  • 817
  • 1
  • 7
  • 9

4 Answers4

44

Starting Scala 2.13, the standard library provides a dedicated resource management utility: Using.

It can be used in this case with scala.io.Source as it extends AutoCloseable in order to read from a file and, no matter what, close the file resource afterwards:

import scala.util.Using
import scala.io.Source

Using(Source.fromFile("file.txt")) { source => source.mkString }
// scala.util.Try[String] = Success("hello\nworld\n")
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
37

For the sake of completeness

val testTxtSource = scala.io.Source.fromFile("test.txt")
val str = testTxtSource.mkString()
testTxtSource.close()

Should get things done.

matyjas
  • 578
  • 5
  • 4
  • 14
    Shouldn't this be in a `try` block and the `close()` in the `finally` block? – robinst May 23 '12 at 13:28
  • 1
    @robinst That would be a good thing and I agree with Daniel's answer (ie don't use scala.io.Source at all) However, it has come in useful at our Dojos where we do not write production code. Also, I would use an ARM library (rather than `try` `catch`) because `close()` could also throw an exception and I find it awkward to have a `try` `catch` `finally` within a `finally`. – matyjas May 29 '12 at 12:18
  • This approach may cause a resource leak – darw Oct 04 '20 at 12:13
30

Scala's io library was just hack done to provide support for limited needs. There was an effort to provide a well-thought io library to Scala, which is currently hosted at assembla, with a github repository as well.

If you are going to use I/O for anything more than reading the occasional file on short-lived processes, you'd better either use Java libraries, or look at the I/O support presently available in the compiler (which will require scala-compiler.jar to be distributed with the app).

Automatic resource management is provided since Scala 2.13 in the standard library (scala.util.Using). For older Scala versions look at this question, or at this library (which is featured in the accepted answer at that question).

So, on Scala 2.13 or newer, this works correctly:

import scala.util.Using
import scala.io.Source

val tryStr: Try[String] = Using(Source.fromFile("test.txt"))(_.mkString())
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 5
    Would you still recommend not using scala.io as of 2016? – knub Mar 11 '16 at 13:08
  • 4
    @knub I haven't look at Scala 2.12 to see if something was improved there, but, otherwise, yes, I stand by the same recommendation. – Daniel C. Sobral Mar 16 '16 at 03:48
  • 2
    In 2019, I am still seeing that the Java NIO.2 package is significantly more sophisticated than the io.Source package, to the point that it warrants use a Java library over a pure Scala solution (as much as that pains me to say). – Janac Meena Mar 24 '19 at 01:38
  • Would it be better to do something like Using(new java.io.BufferedReader(new java.io.FileReader(infile)) { in => val data = in.lines().iterator().asScala // do stuff here ??? It takes quite a few transforms to get the file into a stream of lines and finally into a form that you can do standard Scala operations on. Is there a better way if you want to use modern 2.13 and Java 11+? – Murray Todd Williams Apr 27 '21 at 19:39
  • @MurrayToddWilliams There are more compact ways with recent Java, but that would be a different question. Scala 2.13 added resource management to the standard library (see https://stackoverflow.com/a/55440868/53013), which makes a solution such as proposed by Valentin Tihomirov (https://stackoverflow.com/a/33972743/53013) a practical way of doing it in Scala. – Daniel C. Sobral May 19 '21 at 17:27
13

I recommend using the using, which makes your code neater and more reliable

using(Source.fromFile("test.txt")){ _.mkString()}

Scala 2.13 added Using to the library. So, with the proviso that Using.apply returns a Try, on Scala 2.13 it becomes:

import scala.util.Using
import scala.io.Source

Using(Source.fromFile("test.txt"))(_.mkString)
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 3
    @OsskarWerrewka mkString has no side effects, so the parentheses should be omitted, i.e. `using(Source.fromFile("test.txt")){ _.mkString}` – rbrisk Oct 04 '18 at 13:09