17

I'm using Scala Source.fromFile however I can't seem to find a nice way of getting it to close the underlying InputStream once the file has been read.

Here's my code that will fail with an AssertionError because the file cannot be deleted.

  def main(args : Array[String]) : Unit = {

    val myFile = new File("c:/tmp/doodah.txt")
    var src = Source.fromFile(myFile)
    src.getLines.foreach(l => print(l))

    val deleted: Boolean = myFile.delete

    assert (deleted , "File was not deleted - maybe the stream hasn't been closed in Source")

  }

Source has a method called reset however all that this does is recreate the source from the file.

Internally Source creates an underlying BufferedSource that has a close method. However this is not exposed from Source.

I'd hope that Source would release the file handle once the contents of the file had been read but it doesn't seem to do that.

The best workaround I've seen so far is to essentially cast the Source to a BufferedSource and call close.

try {
  src.getLines.foreach(l => print(l))
}
finally src match { case b: scala.io.BufferedSource => b.close }

Alternatively I could create a Source from an InputStream and manage the closing myself.

However this seems somewhat dirty. How are you supposed to release the file handle when using Source?

pjp
  • 17,039
  • 6
  • 33
  • 58

3 Answers3

17

Scala.io._ is a barebones hack created for the sole purpose of supporting the XML library and the compiler. It is badly designed and suffers from many problems. Scala 2.8 will purport an improved version of it, though hardly anything to write home about.

There is an on-going third party effort by interested parties to develop a sound Scala I/O library. It aims to take home the lessons learned by the JDK7 I/O redesign, while providing an Scala-ish API.

Meanwhile... use Java libraries whenever your application stumbles upon the design problems of the present library.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
6

Works in Scala 2.8.x with a little modification. "getLines" -> "getLines()"

...
src.getLines().foreach(l => print(l))
...

scala.io.Source in 2.8.x is better thought out than its counterpart in 2.7.x and it closes the InputStream correctly.

Walter Chang
  • 11,547
  • 2
  • 47
  • 36
  • Why do you need to use the empty parens in 2.8? – pjp Nov 19 '09 at 14:05
  • 1
    Cos the `getLines` method has an argument in 2.8 which defaults to the platforms's EOL character. Note that default arguments have been added in 2.8 – oxbow_lakes Nov 19 '09 at 14:41
  • Note that Source.getLines is broken at the moment in Scala 2.9.0.1: https://issues.scala-lang.org/browse/SI-4662 – Dan Halbert Jun 30 '11 at 15:34
  • 2
    Why do you think `Source` closes its input stream? AFAIK, it doesn't. I just looked the source code and couldn't find anything that could do that, and a trivial test from REPL shows that the file descriptor remains open even after the iterator is empty. – Daniel C. Sobral Apr 13 '12 at 12:43
4

as far as I can tell io.Source is still broken in 2.8 (connections are leaked etc), so people should be discouraged from using it.

As David suggested above, until the replacement lib http://github.com/scala-incubator/scala-io gets merged (probably after 2.8) the best choice is to relay on pure java libs like apache commons-io etc.

poko
  • 258
  • 2
  • 8