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

Nic*_*cue 89 scala

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

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

Rex*_*err 109

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

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

您可以收集所有文件,然后使用正则表达式进行过滤:

myBigFileArray.filter(f => """.*\.html$""".r.findFirstIn(f.getName).isDefined)
Run Code Online (Sandbox Code Playgroud)

或者您可以将正则表达式合并到递归搜索中:

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))
}
Run Code Online (Sandbox Code Playgroud)

  • 警告:我运行此代码,有时f.listFiles返回null(不知道为什么,但在我的Mac上它确实)和recursiveListFiles函数崩溃.我没有足够的经验在scala中构建一个优雅的null检查,但如果这些== null为我工作,则返回一个空数组. (7认同)
  • @Peter Schwarz - 你_still_需要空检查,因为`f.isDirectory`可能返回true但是`f.listFiles`返回'null`.例如,如果您没有读取文件的权限,您将获得"null".我只是添加一个空检查,而不是两个检查. (4认同)
  • @Jan - 如果`f`没有指向目录或者存在IO错误(至少根据Java规范),`listFiles`返回`null`.添加空检查对于生产使用来说可能是明智的. (2认同)
  • 关于空检查,最惯用的方法是将空值转换为选项并使用映射。所以赋值是 val these = Option(f.listFiles) 并且 ++ 运算符在一个 map 操作中,最后是一个 'getOrElse' (2认同)

yur*_*ura 45

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

import scala.collection.JavaConversions._

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

搜索示例

getFileTree(new File("c:\\main_dir")).filter(_.getName.endsWith(".scala")).foreach(println)
Run Code Online (Sandbox Code Playgroud)

  • @Daniel它不是绝对严格,它懒洋洋地递送目录. (7认同)
  • 替代语法:`def getFileTree(f:File):Stream [File] = f#:: Option(f.listFiles()).toStream.flatten.flatMap(getFileTree)` (4认同)
  • 我同意你的意图,但这个解决方案毫无意义.listFiles()已经返回一个经过全面评估的数组,然后你就可以"懒惰地"评估toStream.您需要一个流表单临时,查找java.nio.file.DirectoryStream. (3认同)
  • 我现在将在我的无限文件系统上尝试:-) (2认同)

Phi*_*hil 20

for (file <- new File("c:\\").listFiles) { processFile(file) }
Run Code Online (Sandbox Code Playgroud)

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

  • 这只做一个级别; 它不会递归到c:\中的目录中. (16认同)

mon*_*onj 20

从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)
Run Code Online (Sandbox Code Playgroud)

您还要求文件匹配.尝试java.nio.file.Files.findjava.nio.file.Files.newDirectoryStream

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


Art*_*mGr 11

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

我会考虑使用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))
}
Run Code Online (Sandbox Code Playgroud)


Dun*_*gor 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))
  })
Run Code Online (Sandbox Code Playgroud)

现在我们可以列出文件

tree(new File(".")).filter(f => f.isFile && f.getName.endsWith(".html")).foreach(println)
Run Code Online (Sandbox Code Playgroud)

或者实现整个流以供以后处理

tree(new File("dir"), true).toArray
Run Code Online (Sandbox Code Playgroud)


Ren*_*aud 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 =>

}
Run Code Online (Sandbox Code Playgroud)


小智 5

我个人喜欢 @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)
}
Run Code Online (Sandbox Code Playgroud)


Phi*_*hil 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")) 
Run Code Online (Sandbox Code Playgroud)