What's the right way to use scala.io.Source?
Asked Answered
V

4

52

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?

Viceroy answered 16/12, 2010 at 8:45 Comment(3)
Is it right to use Source this way not closing the underlying stream?Viceroy
If you read the code of scala.io.Source, you will find that in fact it leaves the work of closing underlying stream to you. Amazing!Viceroy
Consider instead using: import java.nio.file.{Files, Path, Path} val data = Files.readString(path)Innovate
M
47

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")
Micron answered 31/3, 2019 at 12:22 Comment(1)
Nice syntax, seems odd the language had to wait until so late to get this!Rapid
G
37

For the sake of completeness

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

Should get things done.

Geoid answered 24/8, 2011 at 8:4 Comment(3)
Shouldn't this be in a try block and the close() in the finally block?Evocation
@Evocation 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.Geoid
This approach may cause a resource leakSelfcentered
D
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())
Durware answered 22/12, 2010 at 14:10 Comment(5)
Would you still recommend not using scala.io as of 2016?Engrain
@Engrain I haven't look at Scala 2.12 to see if something was improved there, but, otherwise, yes, I stand by the same recommendation.Durware
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).Minuscule
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+?Chaldron
@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://mcmap.net/q/353850/-quot-using-quot-function), which makes a solution such as proposed by Valentin Tihomirov (https://mcmap.net/q/342589/-what-39-s-the-right-way-to-use-scala-io-source) a practical way of doing it in Scala.Durware
U
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)
Univalve answered 28/11, 2015 at 14:59 Comment(1)
@OsskarWerrewka mkString has no side effects, so the parentheses should be omitted, i.e. using(Source.fromFile("test.txt")){ _.mkString}Hasin

© 2022 - 2024 — McMap. All rights reserved.