Is there a good \"scala-esque\" (I guess I mean functional) way of recursively listing files in a directory? What about matching a particular pattern?
For example re
You can use tail recursion for it:
object DirectoryTraversal {
import java.io._
def main(args: Array[String]) {
val dir = new File("C:/Windows")
val files = scan(dir)
val out = new PrintWriter(new File("out.txt"))
files foreach { file =>
out.println(file)
}
out.flush()
out.close()
}
def scan(file: File): List[File] = {
@scala.annotation.tailrec
def sc(acc: List[File], files: List[File]): List[File] = {
files match {
case Nil => acc
case x :: xs => {
x.isDirectory match {
case false => sc(x :: acc, xs)
case true => sc(acc, xs ::: x.listFiles.toList)
}
}
}
}
sc(List(), List(file))
}
}
And here's a mixture of the stream solution from @DuncanMcGregor with the filter from @Rick-777:
def tree( root: File, descendCheck: File => Boolean = { _ => true } ): Stream[File] = {
require(root != null)
def directoryEntries(f: File) = for {
direntries <- Option(f.list).toStream
d <- direntries
} yield new File(f, d)
val shouldDescend = root.isDirectory && descendCheck(root)
( root.exists, shouldDescend ) match {
case ( false, _) => Stream.Empty
case ( true, true ) => root #:: ( directoryEntries(root) flatMap { tree( _, descendCheck ) } )
case ( true, false) => Stream( root )
}
}
def treeIgnoringHiddenFilesAndDirectories( root: File ) = tree( root, { !_.isHidden } ) filter { !_.isHidden }
This gives you a Stream[File] instead of a (potentially huge and very slow) List[File] while letting you decide which sorts of directories to recurse into with the descendCheck() function.
Scala is a multi-paradigm language. A good "scala-esque" way of iterating a directory would be to reuse an existing code!
I'd consider using commons-io a perfectly scala-esque way of iterating a directory. You can use some implicit conversions to make it easier. Like
import org.apache.commons.io.filefilter.IOFileFilter
implicit def newIOFileFilter (filter: File=>Boolean) = new IOFileFilter {
def accept (file: File) = filter (file)
def accept (dir: File, name: String) = filter (new java.io.File (dir, name))
}
No-one has mentioned yet https://github.com/pathikrit/better-files
val dir = "src"/"test"
val matches: Iterator[File] = dir.glob("**/*.{java,scala}")
// above code is equivalent to:
dir.listRecursively.filter(f => f.extension ==
Some(".java") || f.extension == Some(".scala"))
As of Java 1.7 you all should be using java.nio. It offers close-to-native performance (java.io is very slow) and has some useful helpers
But Java 1.8 introduces exactly what you are looking for:
import java.nio.file.{FileSystems, Files}
import scala.collection.JavaConverters._
val dir = FileSystems.getDefault.getPath("/some/path/here")
Files.walk(dir).iterator().asScala.filter(Files.isRegularFile(_)).foreach(println)
You also asked for file matching. Try java.nio.file.Files.find
and also java.nio.file.Files.newDirectoryStream
See documentation here: http://docs.oracle.com/javase/tutorial/essential/io/walk.html
How about
def allFiles(path:File):List[File]=
{
val parts=path.listFiles.toList.partition(_.isDirectory)
parts._2 ::: parts._1.flatMap(allFiles)
}