在Scala中递归删除目录

时间:2014-09-23 15:40:51

标签: java scala file

我正在编写以下内容(使用Scala 2.10和Java 6):

import java.io._

def delete(file: File) {
  if (file.isDirectory) 
    Option(file.listFiles).map(_.toList).getOrElse(Nil).foreach(delete(_))
  file.delete
}

你会如何改进它?代码似乎有效,但忽略了java.io.File.delete的返回值。使用scala.io代替java.io可以更轻松地完成吗?

13 个答案:

答案 0 :(得分:22)

尝试此代码,如果失败则抛出异常:

def deleteRecursively(file: File): Unit = {
  if (file.isDirectory) {
    file.listFiles.foreach(deleteRecursively)
  }
  if (file.exists && !file.delete) {
    throw new Exception(s"Unable to delete ${file.getAbsolutePath}")
  }
}

如果要为所有删除返回值,也可以折叠或映射删除。

答案 1 :(得分:19)

使用scala IO

import scalax.file.Path

val path = Path.fromString("/tmp/testfile")    
try {
  path.deleteRecursively(continueOnFailure = false) 
} catch {
  case e: IOException => // some file could not be deleted
}

或更好,您可以使用Try

val path: Path = Path ("/tmp/file")
Try(path.deleteRecursively(continueOnFailure = false))

将导致Success[Int]包含已删除的文件数或Failure[IOException]

答案 2 :(得分:11)

http://alvinalexander.com/blog/post/java/java-io-faq-how-delete-directory-tree

使用Apache Common IO

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
public void deleteDirectory(String directoryName)
throws IOException
{
  try
  {
    FileUtils.deleteDirectory(new File(directoryName));
  }
  catch (IOException ioe)
  {
    // log the exception here
    ioe.printStackTrace();
    throw ioe;
  }
}

Scala可以做到这一点......

import org.apache.commons.io.FileUtils
import org.apache.commons.io.filefilter.WildcardFileFilter
FileUtils.deleteDirectory(new File(outputFile))

Maven Repo Imports

答案 3 :(得分:10)

使用纯Scala + Java方式

import scala.reflect.io.Directory

val directory = new Directory(new File("/sampleDirectory"))
directory.deleteRecursively()
  

deleteRecursively()失败时返回false

答案 4 :(得分:3)

使用Java NIO.2 API:

import java.nio.file.{Files, Paths, Path, SimpleFileVisitor, FileVisitResult}
import java.nio.file.attribute.BasicFileAttributes

def remove(root: Path): Unit = {
  Files.walkFileTree(root, new SimpleFileVisitor[Path] {
    override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = {
      Files.delete(file)
      FileVisitResult.CONTINUE
    }
    override def postVisitDirectory(dir: Path, exc: IOException): FileVisitResult = {
      Files.delete(dir)
      FileVisitResult.CONTINUE
    }
  })
}

remove(Paths.get("/tmp/testdir"))

真的,遗憾的是NIO.2 API与我们在一起很多年了,但很少有人使用它,即使它真的优于旧的File API。

答案 5 :(得分:2)

使用java 6而不使用依赖关系这几乎是唯一的方法 你的函数的问题是它返回Unit(我将使用def delete(file: File): Unit = {明确注意它

我拿了你的代码并修改它以将地图从文件名返回到删除状态。

def delete(file: File): Array[(String, Boolean)] = {
  Option(file.listFiles).map(_.flatMap(f => delete(f))).getOrElse(Array()) :+ (file.getPath -> file.delete)
}

答案 6 :(得分:2)

添加Slavik Muz的回答:

def deleteFile(file: File): Boolean = {

  def childrenOf(file: File): List[File] = Option(file.listFiles()).getOrElse(Array.empty).toList

  @annotation.tailrec
  def loop(files: List[File]): Boolean = files match {

    case Nil ⇒ true

    case child :: parents if child.isDirectory && child.listFiles().nonEmpty ⇒
      loop((childrenOf(child) :+ child) ++ parents)

    case fileOrEmptyDir :: rest ⇒
      println(s"deleting $fileOrEmptyDir")
      fileOrEmptyDir.delete()
      loop(rest)

  }

  if (!file.exists()) false
  else loop(childrenOf(file) :+ file)
}

答案 7 :(得分:1)

这个使用java.io,但可以删除与通配符字符串匹配的目录,通配符字符串可能包含也可能不包含任何内容。

 for (file <- new File("<path as String>").listFiles;
        if( file.getName() matches("[1-9]*"))) FileUtils.deleteDirectory(file)

目录结构,例如 * A / 1 /,A / 2 /,A / 300 / ...这就是为什么正则表达式字符串:[1-9] *,在scala中找不到支持正则表达式的文件API(可能是我错过了什么)

答案 8 :(得分:1)

变得有点冗长,但这里结合了Garrette解决方案的递归性和原始问题的npe-safety。

def deleteFile(path: String) = {
  val penultimateFile = new File(path.split('/').take(2).mkString("/"))
  def getFiles(f: File): Set[File] = {
    Option(f.listFiles)
      .map(a => a.toSet)
      .getOrElse(Set.empty)
  }
  def getRecursively(f: File): Set[File] = {
    val files = getFiles(f)
    val subDirectories = files.filter(path => path.isDirectory)
    subDirectories.flatMap(getRecursively) ++ files + penultimateFile
  }
  getRecursively(penultimateFile).foreach(file => {
    if (getFiles(file).isEmpty && file.getAbsoluteFile().exists) file.delete
  })
}

答案 9 :(得分:1)

这是一个递归方法,可以清除目录中的所有内容,并返回已删除文件的数量

def cleanDir(dir: File): Int = {

      @tailrec
      def loop(list: Array[File], deletedFiles: Int): Int = {
        if (list.isEmpty) deletedFiles
        else {
          if (list.head.isDirectory && !list.head.listFiles().isEmpty) {
            loop(list.head.listFiles() ++ list.tail ++ Array(list.head), deletedFiles)
          } else {
            val isDeleted = list.head.delete()
            if (isDeleted) loop(list.tail, deletedFiles + 1)
            else loop(list.tail, deletedFiles)
          }
        }
      }

      loop(dir.listFiles(), 0)
    }

答案 10 :(得分:1)

扩展弗拉基米尔·马特维耶夫的NIO2解决方案:

object Util {
  import java.io.IOException
  import java.nio.file.{Files, Paths, Path, SimpleFileVisitor, FileVisitResult}
  import java.nio.file.attribute.BasicFileAttributes

  def remove(root: Path, deleteRoot: Boolean = true): Unit =
    Files.walkFileTree(root, new SimpleFileVisitor[Path] {
      override def visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult = {
        Files.delete(file)
        FileVisitResult.CONTINUE
      }

      override def postVisitDirectory(dir: Path, exception: IOException): FileVisitResult = {
        if (deleteRoot) Files.delete(dir)
        FileVisitResult.CONTINUE
      }
    })

  def removeUnder(string: String): Unit = remove(Paths.get(string), deleteRoot=false)

  def removeAll(string: String): Unit = remove(Paths.get(string))

  def removeUnder(file: java.io.File): Unit = remove(file.toPath, deleteRoot=false)

  def removeAll(file: java.io.File): Unit = remove(file.toPath)
}

答案 11 :(得分:1)

我最终得到的结果

def deleteRecursively(f: File): Boolean = {
  if (f.isDirectory) f.listFiles match {
    case files: Array[File] => files.foreach(deleteRecursively)
    case null =>
  }
  f.delete()
}

答案 12 :(得分:1)

os-lib使得使用单线递归删除变得容易:

os.remove.all(os.pwd/"dogs")

os-lib在后台使用java.nio,只是没有暴露所有Java的丑陋之处。有关如何使用该库的更多信息,请参见here