Есть ли хороший "scala-esque" (я имею в виду функциональный) способ рекурсивного перечисления файлов в каталоге? А как насчет соответствия определенному шаблону?
Например, рекурсивно все файлы, соответствующие "a*.foo"
в c:\temp
.
Код 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))
}
listFiles
возвращается,null
еслиf
не указывает на каталог или если есть ошибка ввода-вывода (по крайней мере, согласно спецификации Java). Добавление нулевой проверки, вероятно, целесообразно для производственного использования.f.isDirectory
вернуть true, ноf.listFiles
можно вернутьnull
. Например, если у вас нет разрешения на чтение файлов, вы получите расширениеnull
. Вместо того, чтобы иметь обе проверки, я бы просто добавил одну нулевую проверку.f.listFiles
возвращает null, когда!f.isDirectory
.Я бы предпочел решение с потоками, потому что вы можете перебирать бесконечную файловую систему (потоки - это коллекции с ленивой оценкой)
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)
источник
def getFileTree(f: File): Stream[File] = f #:: Option(f.listFiles()).toStream.flatten.flatMap(getFileTree)
Начиная с 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
источник
for (file <- new File("c:\\").listFiles) { processFile(file) }
http://langref.org/scala+java/files
источник
Scala - это мультипарадигмальный язык. Хорошим способом итерации каталога в стиле «scala-esque» было бы повторное использование существующего кода!
Я бы подумал, что использование commons-io - это идеальный способ итерации каталога в стиле Scala. Чтобы упростить задачу, вы можете использовать некоторые неявные преобразования. подобно
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)) }
источник
Мне нравится решение yura stream, но оно (и другие) рекурсивно перемещается в скрытые каталоги. Мы также можем упростить, используя тот факт, что
listFiles
возвращает null для не-каталога.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
источник
FileUtils Apache Commons Io умещается в одну строку и вполне читается:
import scala.collection.JavaConversions._ // important for 'foreach' import org.apache.commons.io.FileUtils FileUtils.listFiles(new File("c:\temp"), Array("foo"), true).foreach{ f => }
источник
Никто еще не упомянул 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"))
источник
Взгляните на scala.tools.nsc.io
Там есть несколько очень полезных утилит, включая функции глубокого листинга в классе Directory.
Если я правильно помню, это было выделено (возможно, внесено) ретронимом и рассматривалось как временная остановка до того, как io получит новую и более полную реализацию в стандартной библиотеке.
источник
А вот смесь потокового решения от @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], в то же время позволяя вам решить, какие типы каталогов рекурсивно переходить с помощью функции DescendCheck ().
источник
Как насчет
def allFiles(path:File):List[File]= { val parts=path.listFiles.toList.partition(_.isDirectory) parts._2 ::: parts._1.flatMap(allFiles) }
источник
В Scala есть библиотека scala.reflect.io, которая считается экспериментальной, но выполняет свою работу.
import scala.reflect.io.Path Path(path) walkFilter { p => p.isDirectory || """a*.foo""".r.findFirstIn(p.name).isDefined }
источник
Мне лично нравится элегантность и простота решения, предложенного @Rex Kerr. Но вот как может выглядеть хвостовая рекурсивная версия:
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) }
источник
Вот решение, похожее на решение Рекса Керра, но с фильтром файлов:
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)) }
Метод возвращает List [File], что немного удобнее, чем Array [File]. Он также игнорирует все скрытые каталоги (т. Е. Начинающиеся с '.').
Частично применяется с помощью выбранного вами файлового фильтра, например:
val srcDir = new File( ... ) val htmlFiles = findFiles( _.getName endsWith ".html" )( srcDir )
источник
Самое простое решение, предназначенное только для Scala (если вы не возражаете, если вам потребуется библиотека компилятора Scala):
val path = scala.reflect.io.Path(dir) scala.tools.nsc.io.Path.onlyFiles(path.walk).foreach(println)
В противном случае решение @Renaud будет коротким и приятным (если вы не против использовать Apache Commons FileUtils):
import scala.collection.JavaConversions._ // enables foreach import org.apache.commons.io.FileUtils FileUtils.listFiles(dir, null, true).foreach(println)
Где
dir
находится java.io.File:new File("path/to/dir")
источник
scala-io
Библиотеку из scala-incubrator вроде бы никто не упоминает ...import scalax.file.Path Path.fromString("c:\temp") ** "a*.foo"
Или с
implicit
import scalax.file.ImplicitConversions.string2path "c:\temp" ** "a*.foo"
Или, если вы хотите
implicit
явно ...import scalax.file.Path import scalax.file.ImplicitConversions.string2path val dir: Path = "c:\temp" dir ** "a*.foo"
Документация доступна здесь: http://jesseeichar.github.io/scala-io-doc/0.4.3/index.html#!/file/glob_based_path_sets
источник
У меня работает это заклинание:
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]())(_ ++ _) } }
источник
Для этого можно использовать хвостовую рекурсию:
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)) } }
источник
Почему вы используете файл Java вместо AbstractFile в Scala?
В Scala AbstractFile поддержка итератора позволяет написать более сжатую версию решения Джеймса Мура:
import scala.reflect.io.AbstractFile def tree(root: AbstractFile, descendCheck: AbstractFile => Boolean = {_=>true}): Stream[AbstractFile] = if (root == null || !root.exists) Stream.empty else (root.exists, root.isDirectory && descendCheck(root)) match { case (false, _) => Stream.empty case (true, true) => root #:: root.iterator.flatMap { tree(_, descendCheck) }.toStream case (true, false) => Stream(root) }
источник