100 votes

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

Existe-t-il un bon moyen "scala-esque" (je suppose que je veux dire fonctionnel) de lister récursivement les fichiers d'un répertoire ? Qu'en est-il de la correspondance avec un modèle particulier ?

Par exemple, tous les fichiers correspondant à "a*.foo" en c:\temp .

1 votes

os-lib fournit une interface Scala élégante pour les opérations sur le système de fichiers, comme la liste récursive des fichiers qui correspondent à un motif. Voir ma réponse pour une solution simple. Les développeurs Scala n'ont pas besoin de souffrir avec les bibliothèques de bas niveau java.io et java.nio qui vous obligent à écrire du code inutilement verbeux et complexe.

123voto

Rex Kerr Points 94401

Le code Scala utilise généralement des classes Java pour gérer les E/S, y compris la lecture des répertoires. Vous devez donc 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 rassembler tous les fichiers et les filtrer à l'aide d'une expression rationnelle :

myBigFileArray.filter(f => """.*\.html$""".r.findFirstIn(f.getName).isDefined)

Vous pouvez également incorporer les expressions rationnelles 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))
}

7 votes

ATTENTION : J'ai exécuté ce code et parfois f.listFiles retourne null (je ne sais pas pourquoi mais sur mon mac c'est le cas) et la fonction recursiveListFiles se plante. Je ne suis pas assez expérimenté pour construire une vérification élégante de null en scala, mais retourner un tableau vide si ces ==null a fonctionné pour moi.

2 votes

@Jan - listFiles retours null si f ne pointe pas vers un répertoire ou s'il y a une erreur IO (du moins selon les spécifications Java). L'ajout d'une vérification de la nullité est probablement judicieux pour une utilisation en production.

0 votes

@Rex Peut-être mieux que le null serait d'avoir un contrôle de bon sens pour vérifier que les f est un répertoire au début de la fonction. Cela faciliterait la lecture, dans la mesure où la signification du contrôle serait très claire. Par exemple if (!f.isDirectory) return Array()

48voto

yura Points 5880

Je préférerais une solution avec des flux car vous pouvez itérer sur un système de fichiers infini (les flux sont des collections évaluées paresseusement).

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)

4 votes

Syntaxe alternative : def getFileTree(f: File): Stream[File] = f #:: Option(f.listFiles()).toStream.flatten.flatMap(getFileTree)

3 votes

Je suis d'accord avec votre intention, mais votre solution est inutile. listFiles() renvoie déjà un tableau entièrement évalué, que vous évaluez ensuite "paresseusement" sur toStream. Vous avez besoin d'un flux à partir de zéro, cherchez java.nio.file.DirectoryStream.

8 votes

@Daniel ce n'est pas absolument strict, il récure les répertoires paresseusement.

20voto

Phil Points 2347
for (file <- new File("c:\\").listFiles) { processFile(file) }

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

18 votes

Cela ne fait qu'un seul niveau ; il n'y a pas de récursivité dans les répertoires de c : \.

11voto

Duncan McGregor Points 5478

J'aime bien la solution de stream de yura, mais elle (et les autres) récursionne dans les répertoires cachés. Nous pouvons également simplifier en utilisant le fait que listFiles renvoie null si le répertoire n'existe pas.

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

Nous pouvons maintenant dresser la liste des fichiers

tree(new File(".")).filter(f => f.isFile && f.getName.endsWith(".html")).foreach(println)

ou de réaliser l'ensemble du flux pour un traitement ultérieur

tree(new File("dir"), true).toArray

11voto

ArtemGr Points 2659

Scala est un langage multiparadigme. Une bonne façon "scala-esque" d'itérer un répertoire serait de réutiliser un code existant !

J'envisagerais utiliser commons-io une manière parfaitement scala-esque d'itérer un répertoire. Vous pouvez utiliser des conversions implicites pour faciliter les choses. Par exemple

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X