【问题标题】:Scala Source.fromFile(fileName).getLines().next() misses second character in string lineScala Source.fromFile(fileName).getLines().next() 错过字符串行中的第二个字符
【发布时间】:2018-05-24 02:37:22
【问题描述】:

代码如下:

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

build.sbt 文件有这样的内容:

name := "timeSeriesMerge"

version := "0.1"

scalaVersion := "2.11.8"

lines 中调试程序时,我得到这样一个缺少第二个字符的字符串:

"nme := "timeSeriesMerge""

谁能解释这怎么可能?它错过了字符串中的第二个字符。问题出在哪里?它仅错过第一行的第二个字符。

如果有人能解释为什么这段代码会抛出,那就太好了:

java.io.IOException: Stream Closed

【问题讨论】:

  • 第二个:你应该只在使用完lines之后关闭Source
  • 好的。这有帮助。谢谢你。关于我的第一个问题有什么想法吗?

标签: scala file iterator


【解决方案1】:

对于你的问题1,简短回答:直接使用Source.fromFile("build.sbt").getLines()而不使用bufferedSource,因为bufferedSourcelines共享相同的InputStream,在调试时,可能索引已更改toString

完整说明:

这是由于 toString 方法在 debug time 中改变了Stream 的索引在linesbufferedSource 之间共享,我们知道当 IDE 停在 breakpoint 时,它会 invoke variable toString 方法。对于linesbufferedSource 变量,它将调用Iterator.toString,输出如下:

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

和上面的toString方法一样,它会先调用hasNext,也就是说它会在BufferedSource中调用iter.hasNext,但是BufferedSource.iter有问题:

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

当检查hasNext 时,它会从InputStream 读取一个字符。但是这个InputStream 正在与BufferedLineIterator 共享。

对于val lines = bufferedSource.getLines() 正在从BufferedSourceBufferedSource.decachedReader 创建新的BufferedLineIterator,并共享相同的InputStream between BufferedSource

所以如果我们在BufferedSource 上调用了hasNext 方法,它将改变BufferedLineIterator 上的索引。

下面是一个例子来证明这一点:

  //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._

如上例,我们手动调用hasNext方法,所以Stream索引变成了next。

而当我们使用IDE进行调试时,它会随机调用toString,所以可能会导致第二个字符被debug in:

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

【讨论】:

  • 正如@AlexeyRomanov 评论的那样,我需要在完成使用源后关闭它。它解决了我对java.io.IOException: Stream Closed 的问题。如果我直接使用Source.fromFile("build.sbt").getLines()而不使用bufferedSource,我到底要如何关闭这个Source?
  • @VovaPolischuk,没必要自己关闭,离开就好,JVM会帮你搞定的。
  • 我读到jvm关闭后,它会自动关闭打开的文件。你是这个意思吗?那关于jvm长时间运行的情况?如果您能提供一些链接或解释,我将不胜感激
  • @cehengpohi,你可以在这里留下你的cmetsstackoverflow.com/questions/47769957/…
  • @VovaPolischuk,如果您不经常在应用程序中操作文件,则可以将其留给 JVM 处理,如果您需要在应用程序中打开许多文件,因为scala 没有 try with resource 也许你想像这样实现:stackoverflow.com/questions/25634455/…
【解决方案2】:

在这里,您在流关闭后在 Iterator 上调用“next”。

您应该仅在使用完流后关闭它。 因此,正确的语句顺序应该是

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

另外,如果你想遍历整个文件,你需要在循环中调用 next。你可以试试-

    for(line <- lines){
      println(line)
    }

【讨论】:

    猜你喜欢
    • 2014-10-31
    • 2020-02-14
    • 2018-04-23
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    • 1970-01-01
    • 2021-04-09
    相关资源
    最近更新 更多