web-dev-qa-db-fra.com

Comment lister tous les fichiers d'un sous-répertoire dans scala?

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.

87
Nick Fortescue

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))
}
108
Rex Kerr

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)
42
yura
for (file <- new File("c:\\").listFiles) { processFile(file) }

http://langref.org/scala+Java/files

21
Phil

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

16
monzonj

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
11
Duncan McGregor

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))
}
11
ArtemGr

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

}
6
Renaud

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")) 
5
Phil

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)
}
3
polbotinka

Que diriez-vous

   def allFiles(path:File):List[File]=
   {    
       val parts=path.listFiles.toList.partition(_.isDirectory)
       parts._2 ::: parts._1.flatMap(allFiles)         
   }
3
Dino Fancellu

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

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 ().

3
James Moore

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.

3
Don Mackenzie

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 )
1
Rick-777

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)

dir est un fichier Java.io.:

new File("path/to/dir")
1
Brent Faust

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

1
draw

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))
  }
}
0
Milind

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]())(_ ++ _)
    }
  }
0
Connor Doyle