Existe-t-il un bon "scala-esque" (je pense que je veux dire fonctionnel) lister récursivement les fichiers dans un répertoire? Qu'en est-il de faire correspondre un modèle particulier?
Par exemple, récursivement tous les fichiers correspondant à "a*.foo"
dans c:\temp
.
Le code Scala utilise généralement des classes Java pour traiter les E/S, y compris les répertoires de lecture. Donc vous devez faire quelque chose comme:
import Java.io.File
def recursiveListFiles(f: File): Array[File] = {
val these = f.listFiles
these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
}
Vous pouvez collecter tous les fichiers puis filtrer à l'aide d'une expression rationnelle:
myBigFileArray.filter(f => """.*\.html$""".r.findFirstIn(f.getName).isDefined)
Ou vous pouvez incorporer la regex dans la recherche récursive:
import scala.util.matching.Regex
def recursiveListFiles(f: File, r: Regex): Array[File] = {
val these = f.listFiles
val good = these.filter(f => r.findFirstIn(f.getName).isDefined)
good ++ these.filter(_.isDirectory).flatMap(recursiveListFiles(_,r))
}
Je préférerais une solution avec Streams car vous pouvez effectuer une itération sur un système de fichiers infini (les Streams sont des collections évaluées paresseuses)
import scala.collection.JavaConversions._
def getFileTree(f: File): Stream[File] =
f #:: (if (f.isDirectory) f.listFiles().toStream.flatMap(getFileTree)
else Stream.empty)
Exemple de recherche
getFileTree(new File("c:\\main_dir")).filter(_.getName.endsWith(".scala")).foreach(println)
for (file <- new File("c:\\").listFiles) { processFile(file) }
Depuis Java 1.7, vous devriez tous utiliser Java.nio. Il offre des performances proches de la réalité (Java.io est très lent) et dispose de quelques aides utiles.
Mais Java 1.8 présente exactement ce que vous recherchez:
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)
Vous avez également demandé une correspondance de fichier. Essayez Java.nio.file.Files.find
et aussi Java.nio.file.Files.newDirectoryStream
Voir la documentation ici: http://docs.Oracle.com/javase/tutorial/essential/io/walk.html
J'aime la solution de flux de yura, mais elle (et les autres) revient dans des répertoires cachés. Nous pouvons également simplifier en utilisant le fait que listFiles
renvoie null pour un non-répertoire.
def tree(root: File, skipHidden: Boolean = false): Stream[File] =
if (!root.exists || (skipHidden && root.isHidden)) Stream.empty
else root #:: (
root.listFiles match {
case null => Stream.empty
case files => files.toStream.flatMap(tree(_, skipHidden))
})
Maintenant nous pouvons lister les fichiers
tree(new File(".")).filter(f => f.isFile && f.getName.endsWith(".html")).foreach(println)
ou réaliser le flux entier pour un traitement ultérieur
tree(new File("dir"), true).toArray
La scala est un langage multi-paradigme. Une bonne méthode "scala-esque" pour itérer un répertoire serait de réutiliser un code existant!
Je considérerais en utilisant commons-io une manière parfaitement scala-esque d'itérer un répertoire. Vous pouvez utiliser certaines conversions implicites pour le rendre plus facile. Comme
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))
}
Apache Commons Io's FileUtils correspond à une seule ligne, et est très lisible:
import scala.collection.JavaConversions._ // important for 'foreach'
import org.Apache.commons.io.FileUtils
FileUtils.listFiles(new File("c:\temp"), Array("foo"), true).foreach{ f =>
}
Personne n'a encore mentionné 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"))
Personnellement, j’aime l’élégance et la simplicité de la solution proposée par @Rex Kerr. Mais voici à quoi pourrait ressembler une version récursive de queue:
def listFiles(file: File): List[File] = {
@tailrec
def listFiles(files: List[File], result: List[File]): List[File] = files match {
case Nil => result
case head :: tail if head.isDirectory =>
listFiles(Option(head.listFiles).map(_.toList ::: tail).getOrElse(tail), result)
case head :: tail if head.isFile =>
listFiles(tail, head :: result)
}
listFiles(List(file), Nil)
}
Que diriez-vous
def allFiles(path:File):List[File]=
{
val parts=path.listFiles.toList.partition(_.isDirectory)
parts._2 ::: parts._1.flatMap(allFiles)
}
Scala a la bibliothèque 'scala.reflect.io' qui considérait expérimentale mais fait le travail
import scala.reflect.io.Path
Path(path) walkFilter { p =>
p.isDirectory || """a*.foo""".r.findFirstIn(p.name).isDefined
}
Et voici un mélange de la solution de flux de @DuncanMcGregor avec le filtre de @ 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 }
Cela vous donne un flux [fichier] au lieu d'une liste (potentiellement énorme et très lente) tout en vous permettant de choisir les types de répertoires dans lesquels vous souhaitez effectuer une récurrence avec la fonction descendCheck ().
Jetez un coup d'œil à scala.tools.nsc.io
Il y a quelques utilitaires très utiles, y compris la fonctionnalité de liste approfondie de la classe Directory.
Si je me souviens bien, cela a été mis en évidence (éventuellement contribué) par rétronyme et a été perçu comme un palliatif avant qu'io ait une nouvelle implémentation dans la bibliothèque standard.
Voici une solution similaire à celle de Rex Kerr, mais intégrant un filtre de fichier:
import Java.io.File
def findFiles(fileFilter: (File) => Boolean = (f) => true)(f: File): List[File] = {
val ss = f.list()
val list = if (ss == null) {
Nil
} else {
ss.toList.sorted
}
val visible = list.filter(_.charAt(0) != '.')
val these = visible.map(new File(f, _))
these.filter(fileFilter) ++ these.filter(_.isDirectory).flatMap(findFiles(fileFilter))
}
La méthode retourne une liste [File], ce qui est légèrement plus pratique que Array [File]. Il ignore également tous les répertoires cachés (c'est-à-dire en commençant par '.').
Il est partiellement appliqué à l'aide d'un filtre de fichier de votre choix, par exemple:
val srcDir = new File( ... )
val htmlFiles = findFiles( _.getName endsWith ".html" )( srcDir )
La solution Scala la plus simple (si vous n’avez pas besoin de la bibliothèque du compilateur Scala):
val path = scala.reflect.io.Path(dir)
scala.tools.nsc.io.Path.onlyFiles(path.walk).foreach(println)
Sinon, la solution de @ Renaud est courte et agréable (si vous voulez bien insérer Apache Commons FileUtils):
import scala.collection.JavaConversions._ // enables foreach
import org.Apache.commons.io.FileUtils
FileUtils.listFiles(dir, null, true).foreach(println)
Où dir
est un fichier Java.io.:
new File("path/to/dir")
Il semble que personne ne mentionne la bibliothèque scala-io
de scala-incubrator ...
import scalax.file.Path
Path.fromString("c:\temp") ** "a*.foo"
Ou avec implicit
import scalax.file.ImplicitConversions.string2path
"c:\temp" ** "a*.foo"
Ou si vous voulez implicit
explicitement ...
import scalax.file.Path
import scalax.file.ImplicitConversions.string2path
val dir: Path = "c:\temp"
dir ** "a*.foo"
La documentation est disponible ici: http://jesseeichar.github.io/scala-io-doc/0.4.3/index.html#!/file/glob_based_path_sets
Vous pouvez utiliser la récursion de queue pour cela:
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))
}
}
Cette incantation fonctionne pour moi:
def findFiles(dir: File, criterion: (File) => Boolean): Seq[File] = {
if (dir.isFile) Seq()
else {
val (files, dirs) = dir.listFiles.partition(_.isFile)
files.filter(criterion) ++ dirs.toSeq.map(findFiles(_, criterion)).foldLeft(Seq[File]())(_ ++ _)
}
}