2014-09-23 17 views
12

sto scrivendo il seguente (con Scala 2.10 e Java 6):directory ricorsivamente Elimina a Scala

import java.io._ 

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

Come ti migliorarlo? Il codice sembra funzionare ma ignora il valore restituito di java.io.File.delete. Può essere più semplice con scala.io anziché java.io?

+0

Personalmente, sento che per comprensioni sono molto più facili da leggere: 'for (list <- Opzione (file.listFiles()); bambini <- list) delete (child) ' –

+0

Che senso ha creare questa opzione e mapparla? Cosa c'è di sbagliato con semplice "file.listFiles.foreach (cancella)"? –

+1

@ViliusNormantas 'listFiles' può restituire' null' quindi è necessario controllarlo per 'null' o avvolgerlo con' Option'. Inoltre è probabilmente necessario distinguere due casi in bianco e nero: (1) 'listFiles' restituisce una matrice vuota e (2)' listFiles' restituisce null (si è verificato un errore di I/O) – Michael

risposta

14

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

o meglio, si potrebbe usare un Try

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

che sia tradurrà in un Success[Int] contenente il numero di file cancellati, o un Failure[IOException].

+1

Grazie. Non mi piacerebbe comunque utilizzare alcuna dipendenza esterna. – Michael

2

Utilizzando java 6 senza utilizzare le dipendenze, questo è praticamente l'unico modo per farlo.
Il problema con la funzione è che tornare Unità (che btw far notare esplicito utilizzando def delete(file: File): Unit = {

ho preso il codice e modificarlo per tornare mappa dal nome del file per lo stato cancellazione.

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

Una fodera (ok a lungo ammessa;))! – javadba

+0

Questo non funzionerà su directory con collegamenti simbolici. – ine

14

Prova questo codice che genera un'eccezione se fallisce:.

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

si potrebbe anche piegare o mappare il eliminare se si vuole restituire un valore per tutti i eliminazioni

+0

Bella struttura ricorsiva. – javadba

8

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

Utilizzo di Apache comune 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; 
    } 
} 

La Scala si può solo fare questo ...

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

Maven Repo Imports

1

Questo si usa java.io ma si possono eliminare le directory che corrispondono a una stringa jolly che può o non può contenere contenuti al suo interno.

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

Struttura della directory, ad es. * A/1 /, A/2 /, A/300/... ecco perché la regex String: [1-9] *, non è stato possibile trovare un'API di file in scala che supporti regex (potrebbe esserci perso qualcosa).

1

Un po 'troppo lungo, ma eccone uno che combina la ricorsività della soluzione di Garrette con la sicurezza della domanda originale.

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

Questo è il metodo ricorsivo che pulire tutto nella directory, e il conteggio dei file cancellati

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

tornare Per aggiungere alla risposta di 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) 
} 
2

Utilizzando la NIO.2 Java 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")) 

In realtà, è un peccato che l'API NIO.2 sia con noi per così tanti anni eppure poche persone lo stanno utilizzando, anche se è davvero superiore alla vecchia API File.

1

Ampliando soluzione NIO2 di Vladimir Matveev:

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