2012-04-10 38 views
16

我真的很喜歡斯卡拉 - 迭代器上的文件中的所有行的目錄

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

建設遍歷Scala中的一個文件,我想知道是否有使用類似的施工迭代的方式覆蓋目錄中所有文件的行。

這裏一個重要的限制是所有文件加起來會產生堆溢出的空間量。 (想想幾十GB,所以增加堆大小不是一種選擇)作爲一個暫時的解決方案,我一直在把每一個東西都集中到一個文件中,並使用上面這種懶惰的B/C結構。

問題是,這似乎引發了像..我可以連接兩(百)懶惰迭代器,並得到一個非常大,非常懶惰的問題?

回答

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 } 

訣竅是flatMapits for-comprehension syntactic sugar。以上,例如,是以下或多或少相當於:

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

丹尼爾·索布拉爾在下面評論指出,這種方法(和你的問題的代碼)將離開打開的文件。如果這是一次性腳本,或者您只是在REPL中工作,這可能不是什麼大問題。如果遇到問題,可以使用pimp-my-library pattern實現一些基本的資源管理:

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,你會不會擔心被拋在打開的文件。

+0

這是完美的,我只是使用scala repl和基於該代碼位的10gb文件以及內存使用情況幾乎不變。非常感謝! – 2012-04-10 23:19:46

+1

但是請注意,每個文件的「源」沒有關閉。在這種特殊情況下,如果代碼可能觸及數百個文件,則使用某種類型的ARM非常重要。 – 2012-04-11 01:09:31