Как мне перечислить все файлы в подкаталоге в Scala? - PullRequest
88 голосов
/ 14 апреля 2010

Есть ли хороший "scala-esque" (я думаю, я имею в виду функциональный) способ рекурсивного вывода файлов в каталоге? А как насчет соответствия определенного шаблона?

Например, рекурсивно все файлы, соответствующие "a*.foo" в c:\temp.

Ответы [ 19 ]

108 голосов
/ 14 апреля 2010

В коде Scala обычно используются Java-классы для работы с вводом-выводом, включая чтение каталогов. Поэтому вы должны сделать что-то вроде:

import java.io.File
def recursiveListFiles(f: File): Array[File] = {
  val these = f.listFiles
  these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
}

Вы можете собрать все файлы и затем отфильтровать их с помощью регулярного выражения:

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

Или вы можете включить регулярное выражение в рекурсивный поиск:

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))
}
44 голосов
/ 01 сентября 2011

Я бы предпочел решение с потоками, потому что вы можете перебирать бесконечную файловую систему (потоки - это ленивые оцененные коллекции)

import scala.collection.JavaConversions._

def getFileTree(f: File): Stream[File] =
        f #:: (if (f.isDirectory) f.listFiles().toStream.flatMap(getFileTree) 
               else Stream.empty)

Пример поиска

getFileTree(new File("c:\\main_dir")).filter(_.getName.endsWith(".scala")).foreach(println)
20 голосов
/ 01 апреля 2011
for (file <- new File("c:\\").listFiles) { processFile(file) }

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

18 голосов
/ 29 ноября 2015

Начиная с Java 1.7 вы все должны использовать java.nio. Он предлагает производительность, близкую к нативной (java.io очень медленный) и имеет несколько полезных помощников

Но Java 1.8 представляет именно то, что вы ищете:

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)

Вы также запросили соответствие файла. Попробуйте java.nio.file.Files.find, а также java.nio.file.Files.newDirectoryStream

См. Документацию здесь: http://docs.oracle.com/javase/tutorial/essential/io/walk.html

11 голосов
/ 01 декабря 2011

Мне нравится потоковое решение yura, но оно (и другие) возвращается в скрытые каталоги. Мы также можем упростить использование факта, что listFiles возвращает ноль для не-каталога.

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

Теперь мы можем перечислить файлы

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

или реализовать весь поток для последующей обработки

tree(new File("dir"), true).toArray
11 голосов
/ 14 апреля 2010

Scala - это мультипарадигмальный язык. Хороший способ перебора каталога в стиле scala-esque - это повторное использование существующего кода!

Я бы посчитал использование commons-io идеальным способом скалирования каталога. Вы можете использовать некоторые неявные преобразования, чтобы сделать это проще. Как

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))
}
6 голосов
/ 09 марта 2013

Apache Commons Io's FileUtils помещается в одну строку и вполне читабельно:

import scala.collection.JavaConversions._ // important for 'foreach'
import org.apache.commons.io.FileUtils

FileUtils.listFiles(new File("c:\temp"), Array("foo"), true).foreach{ f =>

}
5 голосов
/ 30 апреля 2016

Никто еще не упомянул 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")) 
3 голосов
/ 25 ноября 2012

А вот смесь потокового раствора из @DuncanMcGregor с фильтром из @ 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 }

Это дает вам Stream [File] вместо (потенциально огромного и очень медленного) List [File], в то же время позволяя вам решить, какие виды каталогов использовать в рекурсивном выражении, с помощью функции yieldcheck ().

3 голосов
/ 18 сентября 2013

Как насчет

   def allFiles(path:File):List[File]=
   {    
       val parts=path.listFiles.toList.partition(_.isDirectory)
       parts._2 ::: parts._1.flatMap(allFiles)         
   }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...