0

Here is the code:

val bufferedSource = Source.fromFile("build.sbt")
val lines = bufferedSource.getLines()
bufferedSource.close()
ptintln(lines.next())

build.sbt file has such content:

name := "timeSeriesMerge"

version := "0.1"

scalaVersion := "2.11.8"

While debugging program in lines I get such string with second character missing:

"nme := "timeSeriesMerge""

Can anybody explain how is this possible? It misses the second character in the string. Where is the problem? It misses the second character for first line only.

It also would be cool if somebody could explain why this code throws:

java.io.IOException: Stream Closed

2 Answers2

4

For your question 1, short answer: use Source.fromFile("build.sbt").getLines() directly without using bufferedSource, because bufferedSource and lines sharing same InputStream, in debug time, maybe the index has changed by toString

Full Explanation:

This is caused by toString method has changed the Stream's index that shared between lines and bufferedSource in the debug time , As we know when IDE stop in the breakpoint, it will invoke variable toString methods. like for lines and bufferedSource variables, it will invoke Iterator.toString, output like:

override def toString = (if (hasNext) "non-empty" else "empty")+" iterator"
> bufferedSource: non-empty iterator
> lines: non-empty iterator

As the above toString method, it will invoke hasNext firstly, this means it will invoke iter.hasNext in BufferedSource, but there is a problem for BufferedSource.iter:

  override lazy val iter = (
    Iterator
    continually (codec wrap charReader.read())
    takeWhile (_ != -1)
    map (_.toChar)
  )

when check hasNext it will read a character from InputStream. but this InputStream is sharing with BufferedLineIterator.

for val lines = bufferedSource.getLines() is creating new BufferedLineIterator from the BufferedSource by BufferedSource.decachedReader with sharing same InputStream between BufferedSource.

So if we have invoked hasNext method on BufferedSource, it will change the index on BufferedLineIterator.

Below is an example to demonstrate this:

  //file content: import Settings._
  val bufferedSource: BufferedSource = Source.fromFile("build.sbt")
  val lines = bufferedSource.getLines()
  bufferedSource.hasNext
  val str = lines.next()
  println(str)
  > mport Settings._

As the above example, we manually invoke hasNext method, so the Stream index has changed to next.

And for when we using IDE to debug, it invokes toString randomly, so it maybe cause the second character missing by debug in:

https://github.com/scala/scala/blob/2.13.x/src/library/scala/io/BufferedSource.scala#L55

chengpohi
  • 14,064
  • 1
  • 24
  • 42
  • As @AlexeyRomanov commented i need to close Source after i finished working with it. It resolves my problem with `java.io.IOException: Stream Closed`. If i use `Source.fromFile("build.sbt").getLines()` directly without using bufferedSource, how can i close this Source in the end? – Vova Polischuk Dec 10 '17 at 15:20
  • 1
    @VovaPolischuk, it's unnecessary to close it by yourself, just leave it, **JVM** will help you do this. – chengpohi Dec 11 '17 at 02:04
  • i read that after jvm shut down, it automatically closes open file. Do you mean this? that about the case when jvm is run for a long time? I would appreciate, if you could live some links or explanation about that – Vova Polischuk Dec 12 '17 at 09:04
  • @cehengpohi, you can leave your comments here https://stackoverflow.com/questions/47769957/does-jvm-automatically-closes-files – Vova Polischuk Dec 12 '17 at 10:10
  • @VovaPolischuk, if you don't frequently operate files in your application, you can leave it to **JVM** handle it, if you need open many files in your application, since **scala** doesn't have **try with resource** maybe you want to achieve this like: https://stackoverflow.com/questions/25634455/simple-scala-pattern-for-using-try-with-resources-automatic-resource-manageme/25634594 – chengpohi Dec 12 '17 at 15:48
2

Here, You are calling "next" on Iterator after the stream has been closed.

You should close the stream only after you have done using it. Therefore, the correct sequence of statements should be

    println(lines.next())
    bufferedSource.close()

Also, if you want to iterate over the complete file, you need to call next inside a loop. You can try -

    for(line <- lines){
      println(line)
    }
S.K
  • 480
  • 1
  • 4
  • 19