2012-01-19 28 views
5

我想在Scala中使用新的java.nio.file.Files.walkFileTree。我甚至成功:如何在Scala中使用java.nio.file.Files.walkFileTree

class Visitor 
    extends 
     java.nio.file.SimpleFileVisitor [java.nio.file.Path] 
    { 
    override def visitFile(
     File : java.nio.file.Path, 
     Attrs : java.nio.file.attribute.BasicFileAttributes) : java.nio.file.FileVisitResult = 
    { 
     if (! File.toString.contains(".svn")) 
     { 
     System.out.println(File); 
     } // if 

     java.nio.file.FileVisitResult.CONTINUE; 
    } // visitFile 
} // Visitor 

java.nio.file.Files.walkFileTree (Project_Home, new Visitor) 

但雖然此代碼工作正常,我覺得有點像進入斯卡拉Java範例。所以對真正的斯卡拉大師們提出一個問題:有什麼我可以改進的,還是僅僅是這樣?

回答

6

訪客是真的foreach沒有功能的好處,所以讓我們做一個foreach。該方法是靜態的,但它需要作爲第一個參數Path,所以我們會充實Pathforeach方法,它是用什麼做這樣的:

import java.nio.file._ 
import java.nio.file.attribute.BasicFileAttributes 

implicit def fromNioPath(path: Path): TraverseFiles = new TraversePath(path) 

和其他一切是TraversePath類,在其內部看起來有點像這樣:

class TraversePath(path: Path) { 
    def foreach(f: (Path, BasicFileAttributes) => Unit) { 
    // ... 
    } 
} 

這是足以讓你寫:

ProjectHome foreach ((file, _) => if (!file.toString.contains(".svn")) println(File)) 

當然,實際上並不會做任何事情,讓我們把它做一些事情:

class TraversePath(path: Path) { 
    def foreach(f: (Path, BasicFileAttributes) => Unit) { 
    class Visitor extends SimpleFileVisitor[Path] { 
     override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = try { 
     f(file, attrs) 
     FileVisitResult.CONTINUE 
     } catch { 
     case _ => FileVisitResult.TERMINATE 
     } 
    } 
    Files.walkFileTree(path, new Visitor) 
    } 
} 

在那裏,現在該行會做同樣的事情,你的代碼做了!但是,我們可以進一步改進它。碰巧foreachTraversable所需的唯一方法,所以我們可以擴展該類,並獲得Scala集合的所有方法!

唯一的問題是Traversable.foreach函數只有一個參數,在這裏我們取兩個參數。不過,我們可以將它改爲接收元組。下面是完整的代碼:

import java.nio.file._ 
import java.nio.file.attribute.BasicFileAttributes 
import scala.collection.Traversable 

// Make it extend Traversable 
class TraversePath(path: Path) extends Traversable[(Path, BasicFileAttributes)] { 

    // Make foreach receive a function from Tuple2 to Unit 
    def foreach(f: ((Path, BasicFileAttributes)) => Unit) { 
    class Visitor extends SimpleFileVisitor[Path] { 
     override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = try { 
     // Pass a tuple to f 
     f(file -> attrs) 
     FileVisitResult.CONTINUE 
     } catch { 
     case _ => FileVisitResult.TERMINATE 
     } 
    } 
    Files.walkFileTree(path, new Visitor) 
    } 
} 

ProjectHome foreach { 
    // use case to seamlessly deconstruct the tuple 
    case (file, _) => if (!file.toString.contains(".svn")) println(File) 
} 

免責聲明:我已經測試沒有這個代碼,因爲我安裝了Java 7沒有做。可能有一些錯誤。

2

你可以讓你的代碼更漂亮一點,但在一天結束時它仍然會看起來像普通的舊訪問者模式。

2

這裏是丹尼爾的劇本由編譯:

import java.nio.file._ 
import java.nio.file.attribute.BasicFileAttributes 
import scala.collection.Traversable 

// Make it extend Traversable 
class TraversePath(path: Path) extends Traversable[(Path, BasicFileAttributes)] { 

    // Make foreach receive a function from Tuple2 to Unit 
    def foreach[U](f: ((Path, BasicFileAttributes)) => U) { 
    class Visitor extends SimpleFileVisitor[Path] { 
     override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = try { 
     // Pass a tuple to f 
     f(file -> attrs) 
     FileVisitResult.CONTINUE 
     } catch { 
     case _ => FileVisitResult.TERMINATE 
     } 
    } 
    Files.walkFileTree(path, new Visitor) 
    } 
} 
val projectHome = new TraversePath(Paths.get(".")) 

projectHome foreach { 
    // use case to seamlessly deconstruct the tuple 
    case (file:Path, attr:BasicFileAttributes) => if (!file.toString.contains(".svn")) println(file) 
} 
2

Daniel's answer爲根本,我曾略盡Path訪問方便implicits,因爲你是在收集使用。注意並不是所有的功能都包含在內。

class TraversePath(path: Path) { 
    def foreach(f: (Path, BasicFileAttributes) => Unit) { 
     Files.walkFileTree(path, new SimpleFileVisitor[Path] { 
      override def visitFile(file: Path, attrs: BasicFileAttributes) = { 
       f(file, attrs) 
       FileVisitResult.CONTINUE 
      } 
     }) 
    } 

    /** 
    * foreach that takes FileVisitResult instead of Unit 
    */ 
    def foreach2(f: (Path, BasicFileAttributes) => FileVisitResult) { 
     Files.walkFileTree(path, new SimpleFileVisitor[Path] { 
      override def visitFile(file: Path, attrs: BasicFileAttributes) = f(file, attrs) 
     }) 
    } 

    def foldLeft[T](t: T)(f: (T, Path) => T) = { 
     var current = t 
     foreach((p, _) => current = f(current, p)) 
     current 
    } 

    def forall(f: Path => Boolean) = { 
     var ret = true 
     foreach2((p, _) => 
      if (!f(path)) { 
       ret = false 
       FileVisitResult.TERMINATE 
      } 
      else 
       FileVisitResult.CONTINUE 
     ) 
     ret 
    } 

    def exists(f: Path => Boolean) = { 
     var ret = false 
     foreach2((p, _) => 
      if (f(path)) { 
       ret = true 
       FileVisitResult.TERMINATE 
      } 
      else 
       FileVisitResult.CONTINUE 
     ) 
    } 

    /** 
    * Directly modifies the underlying path. 
    */ 
    def mapReal(f: Path => Path) = foreach((p, _) => Files.move(p, f(p))) 

    /** 
    * @param f map function 
    * @return a left-folded list with the map function applied to each element 
    */ 
    def map(f: Path => Path) = foldLeft(Nil: List[Path]) { 
     case (xs, p) => xs ::: f(p) :: Nil 
    } 

    def find(f: Path => Boolean) = { 
     var k = None: Option[Path] 
     foreach2((p, _) => 
      if (f(p)) { 
       k = Some(p) 
       FileVisitResult.TERMINATE 
      } else FileVisitResult.CONTINUE 
     ) 
     k 
    } 
} 

implicit def fromNioPath(path: Path) = new TraversePath(path) 

的java.nio中的API是非常強大的,並且,恕我直言,對於Scala的使用非常sufficing。有了這些含義(如果你想編寫一些函數,還有更多),完成更難的任務是非常簡單的。

您現在可以通過寫這樣的使用:

val path1 = Paths.get(sys.props("user.home"), "workspace") 

val path2 = Paths.get(sys.props("user.home"), "workspace2") 

val list1 = path1.foldLeft(Nil: List[Path]) { 
    (xs, p) => xs ::: path1.relativize(p) :: Nil 
} 
val list2 = path2.foldLeft(Nil: List[Path]) { 
    (xs, p) => xs ::: path2.relativize(p) :: Nil 
} 
(list1 diff list2) foreach println 

問候,
Danyel

0

FIles.walkFileTree例子來比較兩個目錄/同步兩個目錄 文件差異

private static void compareDirectories(String srcPath, String destPath) throws IOException, InterruptedException { 
    System.out.println("sync. started...."); 
    final Path mainDir = Paths.get(srcPath); 
    final Path otherDir = Paths.get(destPath); 

    // Walk thru mainDir directory 
    Files.walkFileTree(mainDir, new FileVisitor<Path>() { 
     @Override 
     public FileVisitResult preVisitDirectory(Path path, 
       BasicFileAttributes atts) throws IOException { 
      return visitFile(path, atts); 
     } 

     @Override 
     public FileVisitResult visitFile(Path path, BasicFileAttributes mainAtts) 
       throws IOException { 
      // I've seen two implementations on windows and MacOSX. One has passed the relative path, one the absolute path. 
      // This works in both cases 
      Path relativePath = mainDir.relativize(mainDir.resolve(path)); 
      File tmpFile = new File(otherDir+"/"+relativePath); 

       if(tmpFile.exists()) { 
        BasicFileAttributes otherAtts = Files.readAttributes(otherDir.resolve(relativePath), BasicFileAttributes.class); 
        // Do your comparison logic here: we are skipping directories as all directories are traversed automatically 
        if(!new File(path.toString()).isDirectory()) { 
                //write your logic for comparing files 
         compareEntries(mainDir, otherDir, relativePath, mainAtts, otherAtts); 
        } 
        else { 
         File src = new File(path.toString()); 

                //write your logic here for comparing directories 
                compareDirectories(src,tmpFile.toPath().toString()+"/"+s); 
        } 
       } 
       else { 
              //this function will copy missing files in destPath from srcPath recursive function till depth of directory structure 
        copyFolderOrFiles(new File(path.toString()), tmpFile); 
       } 
      return FileVisitResult.CONTINUE; 
     } 

     @Override 
     public FileVisitResult postVisitDirectory(Path path, 
       IOException exc) throws IOException { 
      return FileVisitResult.CONTINUE; 
     } 

     @Override 
     public FileVisitResult visitFileFailed(Path path, IOException exc) 
       throws IOException { 
      exc.printStackTrace(); 
      // If the root directory has failed it makes no sense to continue 
      return (path.equals(mainDir))? FileVisitResult.TERMINATE:FileVisitResult.CONTINUE; 
     } 
    }); 
} 
0

擴展了其他帖子的想法。我喜歡我們可以匹配案例類的解決方案。以下代碼僅返回訪問者被調用的不同事件的字符串集合。

FileWalker(java.nio.file.Paths.get(" your dir ")).map({ 
    case PreVisitDirectory(dir, atts) => s"about to visit dir ${dir}" 
    case PostVisitDirectory(dir, exc) => s"have visited dir ${dir}" 
    case VisitFile(file, attrs) => s"visiting file ${file}" 
    case VisitFileFailed(file, exc) => s"failed to visit ${file}" 
}) 

FileWalker的實現是:

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

trait FileVisitEvent 
case class PreVisitDirectory(path: Path, atts: BasicFileAttributes) extends FileVisitEvent 
case class PostVisitDirectory(dir: Path, exc: IOException) extends FileVisitEvent 
case class VisitFile(file: Path, attrs: BasicFileAttributes) extends FileVisitEvent 
case class VisitFileFailed(file: Path, exc: IOException) extends FileVisitEvent 

/** 
    * Scala style walker for a directory tree 
    * 
    * Is a treversable over the tree which traverses different event types extending {{FileVisitEvent}} 
    * 
    * @param from 
    */ 
class FileWalker(from: Path) extends Traversable[FileVisitEvent] { 
    // just to simplify error handling 
    def wrapper(x: => Unit): FileVisitResult = try { 
    x 
    FileVisitResult.CONTINUE 
    } 
    catch { 
    case _ : Throwable => FileVisitResult.TERMINATE 
    } 

    override def foreach[U](f: (FileVisitEvent) => U): Unit = { 
    Files.walkFileTree(from, new SimpleFileVisitor[Path] { 
     override def preVisitDirectory(dir: Path, atts: BasicFileAttributes): FileVisitResult = 
     wrapper(f(PreVisitDirectory(dir, atts))) 

     override def postVisitDirectory(dir: Path, exc: IOException): FileVisitResult = 
     wrapper(f(PostVisitDirectory(dir, exc))) 

     override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = 
     wrapper(f(VisitFile(file, attrs))) 

     override def visitFileFailed(file: Path, exc: IOException): FileVisitResult = 
     wrapper(f(VisitFileFailed(file, exc))) 
    }) 
    } 
} 

object FileWalker { 
    def apply(from : Path) = new FileWalker(from) 
}