如何列出scala子目录中的所有文件?

时间:2010-04-14 13:21:07

标签: scala

是否有一个好的“scala-esque”(我想我的意思是功能性)以递归方式列出目录中的文件?匹配特定模式怎么样?

例如递归地在"a*.foo"中匹配c:\temp的所有文件。

22 个答案:

答案 0 :(得分:108)

Scala代码通常使用Java类来处理I / O,包括读取目录。所以你必须做类似的事情:

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

答案 1 :(得分:43)

我更喜欢Streams的解决方案,因为你可以迭代无限的文件系统(Streams是懒惰的评估集合)

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)

答案 2 :(得分:20)

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

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

答案 3 :(得分:17)

从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.findjava.nio.file.Files.newDirectoryStream

请参阅此处的文档:http://docs.oracle.com/javase/tutorial/essential/io/walk.html

答案 4 :(得分:11)

Scala是一种多范式语言。迭代目录的一种好的“scala-esque”方式是重用现有代码!

我认为using commons-io是一种完美的scala-esque迭代目录的方式。您可以使用一些隐式转换来简化它。像

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

答案 5 :(得分:11)

我喜欢yura的流解决方案,但它(和其他人)会进入隐藏目录。我们还可以通过利用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

答案 6 :(得分:6)

Apache Commons Io的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 =>

}

答案 7 :(得分:5)

没人提到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")) 

答案 8 :(得分:3)

这是来自@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()函数进行哪些类型的目录。

答案 9 :(得分:3)

看看scala.tools.nsc.io

有一些非常有用的实用程序,包括Directory类的深度列表功能。

如果我没记错的话,这可以通过反义词突出显示(可能有所贡献),并且在io在标准库中获得新的和更完整的实现之前被视为权宜之计。

答案 10 :(得分:3)

怎么样

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

答案 11 :(得分:3)

Scala有库'scala.reflect.io',它考虑了实验,但做了工作

import scala.reflect.io.Path
Path(path) walkFilter { p => 
  p.isDirectory || """a*.foo""".r.findFirstIn(p.name).isDefined
}

答案 12 :(得分:3)

我个人喜欢@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)
}

答案 13 :(得分:1)

最简单的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")

答案 14 :(得分:1)

似乎没有人提到scala-incubrator中的scala-io库......

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

答案 15 :(得分:1)

这里是与Rex Kerr类似的解决方案,但是包含了文件过滤器:

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]稍微方便一些。它还会忽略所有隐藏的目录(即以&#39;。&#39;开头)。

使用您选择的文件过滤器进行部分应用,例如:

val srcDir = new File( ... )
val htmlFiles = findFiles( _.getName endsWith ".html" )( srcDir )

答案 16 :(得分:0)

对接受的答案略有改进。
通过在 _.isDirectory 上分区,此函数仅返回文件列表。
(不包括目录)

import java.io.File
def recursiveListFiles(f: File): Array[File] = {
  val (dir, files)  = f.listFiles.partition(_.isDirectory)
  files ++ dir.flatMap(recursiveListFiles)
}

答案 17 :(得分:0)

scala.reflect.io.Directory 的 deepFiles 方法提供了一种非常好的递归获取目录中所有文件的方法:

import scala.reflect.io.Directory
new Directory(f).deepFiles.filter(x => x.startsWith("a") && x.endsWith(".foo"))

deepFiles 返回一个迭代器,因此如果您不需要/不想要延迟评估,您可以将其转换为其他集合类型。

答案 18 :(得分:0)

os-lib是在Scala中递归列出文件的最简单方法。

os.walk(os.pwd/"countries").filter(os.isFile(_))

以下是递归列出与问题中指定的"a*.foo"模式匹配的所有文件的方法:

os.walk(os.pwd/"countries").filter(_.segments.toList.last matches "a.*\\.foo")

os-lib比其他替代品更优雅,更强大。它返回os个对象,您可以轻松移动,重命名任何对象。您不再需要笨拙的Java库。

如果您想在本地计算机上试用此库,可以运行以下代码段:

os.makeDir(os.pwd/"countries")
os.makeDir(os.pwd/"countries"/"colombia")
os.write(os.pwd/"countries"/"colombia"/"medellin.txt", "q mas pues")
os.write(os.pwd/"countries"/"colombia"/"a_something.foo", "soy un rolo")
os.makeDir(os.pwd/"countries"/"brasil")
os.write(os.pwd/"countries"/"brasil"/"a_whatever.foo", "carnaval")
os.write(os.pwd/"countries"/"brasil"/"a_city.txt", "carnaval")

println(os.walk(os.pwd/"countries").filter(os.isFile(_)))将返回以下内容:

ArraySeq(
  /.../countries/brasil/a_whatever.foo, 
  /.../countries/brasil/a_city.txt, 
  /.../countries/colombia/a_something.foo, 
  /.../countries/colombia/medellin.txt)

os.walk(os.pwd/"countries").filter(_.segments.toList.last matches "a.*\\.foo")将返回以下内容:

ArraySeq(
  /.../countries/brasil/a_whatever.foo, 
  /.../countries/colombia/a_something.foo)

有关如何使用os-lib的更多详细信息,请参见here

答案 19 :(得分:0)

你可以使用尾递归:

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

答案 20 :(得分:0)

这个咒语对我有用:

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

答案 21 :(得分:-1)

为什么使用Java的File而不是Scala的AbstractFile?

使用Scala的AbstractFile,迭代器支持允许编写更简洁的James Moore解决方案版本:

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