Scala - 遍历目录中文件中所有行的迭代器

chu*_*lor 16 io scala lazy-evaluation

我真的很喜欢

for (line <- Source fromFile inputPath getLines) {doSomething line}
Run Code Online (Sandbox Code Playgroud)

用于迭代scala中的文件的构造,我想知道是否有一种方法可以使用类似的构造来迭代目录中所有文件中的行.

这里的一个重要限制是所有文件都会增加一定量的空间,从而产生堆溢出.(想想几十GB,所以增加堆大小不是一个选项)作为暂时的解决方案,我一直在把每个人整合成一个文件,并使用上述结构,这是懒惰的b/c.

重点是,这似乎引起了诸如......之类的问题.我可以连接两个(百个)惰性迭代器并获得一个非常大的,非常懒的吗?

Tra*_*own 27

是的,虽然它不是那么简洁:

import java.io.File
import scala.io.Source

for {
  file <- new File(dir).listFiles.toIterator if file.isFile
  line <- Source fromFile file getLines
} { doSomething line }
Run Code Online (Sandbox Code Playgroud)

诀窍是flatMap它的for-comprehension语法糖.例如,上述内容或多或少等同于以下内容:

new File(dir)
  .listFiles.toIterator
  .filter(_.isFile)
  .flatMap(Source fromFile _ getLines)
  .map(doSomething)
Run Code Online (Sandbox Code Playgroud)

正如Daniel Sobral在下面的评论中指出的那样,这种方法(以及你问题中的代码)将使文件保持打开状态.如果这是一个一次性的脚本,或者你只是在REPL中工作,这可能不是什么大问题.如果遇到问题,可以使用pimp-my-library模式实现一些基本的资源管理:

implicit def toClosingSource(source: Source) = new {
  val lines = source.getLines
  var stillOpen = true
  def getLinesAndClose = new Iterator[String] {
    def hasNext = stillOpen && lines.hasNext
    def next = {
      val line = lines.next
      if (!lines.hasNext) { source.close() ; stillOpen = false }
      line
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在只需使用Source fromFile file getLinesAndClose,您就不必担心文件被打开了.