2014-09-23 8 views
12

piszę następujące (z 2,10 Scala i Java 6):katalogu Delete rekurencyjnie w Scala

import java.io._ 

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

Jak można ją poprawić? Kod wydaje się działać, ale ignoruje wartość zwracaną przez java.io.File.delete. Czy można to łatwiejsze z scala.io zamiast z java.io?

+0

Osobiście uważam, że dla Ułatwienia są znacznie łatwiejsze do odczytania: 'za (lista <- Option (file.listFiles()); dziecko <- lista) usuń (dziecko) ' –

+0

Jaki jest sens stworzenia tej opcji i jej mapowania? Co jest nie tak z prostym "file.listFiles.foreach (delete)"? –

+1

@ViliusNormantas 'listFiles' może zwrócić wartość' null', więc musisz sprawdzić ją dla 'null' lub owiń ją opcją' Option'. Ponadto prawdopodobnie trzeba rozróżnić b/w dwa przypadki: (1) 'listFiles' zwraca pustą tablicę i (2)' listFiles' zwraca null (wystąpił błąd we/wy) – Michael

Odpowiedz

14

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

lub lepiej, można użyć Try

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

które będą albo doprowadzić do Success[Int] zawierającej liczbę plików usunięte lub Failure[IOException].

+1

Dziękuję. Nie chciałbym jednak używać żadnych zewnętrznych zależności. – Michael

2

Używanie języka Java 6 bez korzystania z zależności jest to jedyny sposób, aby to zrobić.
Problem z funkcji jest to, że powrót Unit (które btw byłoby jednoznaczne pamiętać go za pomocą def delete(file: File): Unit = {

Wziąłem swój kod i zmodyfikować go do powrotu mapę z nazwy pliku do statusu usunięciem.

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

Jedna (ok, długo potraktowana;))) wkładka! – javadba

+0

Nie będzie działać na katalogach z dowiązaniami symbolicznymi. – ine

14

Spróbuj tego kodu, który zgłasza wyjątek, jeśli to się nie powiedzie.

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

można również złożyć lub map nad usunąć, jeśli chcesz zwrócić wartość dla wszystkich usuwaniem

+0

Ładna struktura rekurencyjna. – javadba

8

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

Korzystanie Apache Commons 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; 
    } 
} 

Ten Scala może po prostu to zrobić ...

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

Maven Repo Imports

1

Ten wykorzystuje java.io ale można usuwać katalogi pasujące do niego z ciągiem znaków wieloznacznych, który może zawierać lub nie zawierać żadnych treści w nim zawartych.

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

Struktura spisu np. * A/1 /, A/2 /, A/300/... to dlatego ciąg regex: [1-9] *, nie mógł znaleźć pliku interfejsu API w scala, który obsługuje regex (może być pominięte coś).

1

Trochę za długa, ale taka, która łączy rekursywność rozwiązania Garrette'a z bezpieczeństwem oryginalnego pytania.

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

To metody rekurencyjnej, że czyste wszystko w katalogu i liczbę usuniętych plików

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

powrócić do dodać do Slavik MUZ za odpowiedź:

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

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

Naprawdę, szkoda, że ​​API NIO.2 jest z nami od tylu lat i używa go jeszcze niewielu ludzi, mimo że jest naprawdę lepszy od starego API File.

1

Rozszerzając Władimira MATVEEV rozwiązania 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) 
}