Удалить каталог рекурсивно в Scala

Я пишу следующее (с 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?

Ответ 1

Попробуйте этот код, который выдает исключение в случае сбоя:

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

Вы также можете свернуть или отобразить удаление, если хотите вернуть значение для всех удалений.

Ответ 2

Использование 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].

Ответ 3

С 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

Ответ 4

С чистым скала + Java путь

import scala.reflect.io.Directory

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

deleteRecursively() Возвращает false при ошибке

Ответ 5

Использование API Java NIO.2:

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

Действительно, жаль, что API NIO.2 с нами уже столько лет, и все же мало кто использует его, хотя он действительно превосходит старый API File.

Ответ 6

Использование 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)
}

Ответ 7

Чтобы добавить к Славику Музу ответ:

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

Ответ 8

Этот использует 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/... поэтому regex String: [1-9] *, не удалось найти API-интерфейс файла в scala, который поддерживает регулярное выражение (может быть, я пропустил что-то).

Ответ 9

Быть немного длинным, но здесь тот, который сочетает рекурсию решения Гарретта с npe-безопасностью исходного вопроса.

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

Ответ 10

Это рекурсивный метод, который очищает все в каталоге и возвращает количество удаленных файлов

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

Ответ 11

Развертывание решения Владимира Матвеева 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)
}