2012-04-10 15 views
16

me gusta mucho la construcciónScala - iterador sobre todas las líneas de los ficheros en un directorio

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

para iterar sobre un archivo en Scala y estoy preguntando si hay una manera de utilizar una construcción similar a la iteración sobre las líneas en todos los archivos en un directorio.

Una restricción importante aquí es que todos los archivos se suman a una cantidad de espacio que generaría un desbordamiento de heap. (Piensa decenas de GB, por lo que el aumento de tamaño de la pila no es una opción) Como solución, por el momento, he estado cat'ing todos juntos en un solo archivo y el uso de la construcción anterior que funciona b/c de la pereza. ser

punto, esto parece plantear preguntas como .. puedo concatenar dos (cien) iteradores perezosos y conseguir una muy grande, muy vago?

Respuesta

27

Sí, aunque no es tan concisa:

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 } 

El truco es flatMap y its for-comprehension syntactic sugar. Lo anterior, por ejemplo, es más o menos equivalente a la siguiente:

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

Como Daniel Sobral señala en un comentario más abajo, este enfoque (y el código en su pregunta) dejará los archivos abiertos. Si se trata de un script único o si solo está trabajando en el REPL, esto podría no ser un gran problema. Si llegas a tener problemas, se puede utilizar el pimp-my-library pattern para implementar algunas básico de gestión de recursos:

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

Ahora sólo tiene que utilizar Source fromFile file getLinesAndClose y usted no tendrá que preocuparse acerca de los archivos que se dejan abiertas.

+0

que es perfecto, sólo funcionó durante alrededor de 10 GB de archivos utilizando el repl Scala con un bit de código basado en eso y el uso de la memoria apenas se movieron. ¡Muchas gracias! –

+1

Tenga en cuenta, sin embargo, que la 'Fuente' de cada archivo no se cierra. En este caso particular, donde el código puede tocar cientos de archivos, es importante usar algún tipo de ARM. –

Cuestiones relacionadas