18

我真的很喜欢

for (line <- Source fromFile inputPath getLines) {doSomething line}

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

这里的一个重要限制是所有文件加起来都会产生堆溢出的空间量。(想想几十 GB,所以增加堆大小不是一种选择)作为暂时的解决方法,我一直在将每个文件放在一个文件中,并使用上面的结构,它适用于 b/c 的懒惰。

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

4

1 回答 1

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 }

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

new File(dir)
  .listFiles.toIterator
  .filter(_.isFile)
  .flatMap(Source fromFile _ getLines)
  .map(doSomething)

正如 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
    }
  }
}

现在只需使用Source fromFile file getLinesAndClose,您就不必担心文件处于打开状态。

于 2012-04-10T22:28:39.000 回答