2012-01-19 27 views
5

Tôi muốn sử dụng java.nio.file.Files.walkFileTree mới trong Scala. Và tôi thậm chí còn thành công:Cách sử dụng java.nio.file.Files.walkFileTree trong Scala

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) 

Nhưng trong khi mã này hoạt động tốt, tôi cảm thấy giống như mang các mô hình Java vào Scala. Vì vậy, một câu hỏi cho Scala Gurus thực sự: Có bất cứ điều gì tôi có thể cải thiện hay chỉ là nó?

Trả lời

6

Khách truy cập thực sự là foreach mà không có lợi ích của chức năng, vì vậy, hãy tạo một foreach. Phương pháp này là tĩnh, nhưng phải mất như là đối số đầu tiên một Path, vì vậy chúng tôi sẽ làm giàu cho Path với một phương pháp foreach, được thực hiện với một cái gì đó như thế này:

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

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

Và mọi thứ khác là bên trong lớp TraversePath, mà trông hơi như thế này:

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

này là đủ để bạn có thể viết những dòng này:

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

Tất nhiên, nó sẽ không thực sự làm bất cứ điều gì, vì vậy chúng ta hãy làm cho nó làm điều gì đó:

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

Có, bây giờ mà dòng sẽ làm điều tương tự như mã của bạn đã làm! Tuy nhiên, chúng tôi có thể cải thiện hơn nữa. Nó xảy ra rằng foreach là phương pháp duy nhất được yêu cầu của Traversable, vì vậy chúng tôi có thể mở rộng lớp đó và nhận tất cả các phương pháp của bộ sưu tập Scala!

Vấn đề duy nhất là hàm Traversable.foreach chỉ nhận một đối số và ở đây chúng tôi đang lấy hai tham số. Tuy nhiên, chúng ta có thể thay đổi nó thành một tuple. Đây là mã đầy đủ:

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

Tuyên bố từ chối trách nhiệm: Tôi chưa thử nghiệm mã này vì tôi chưa cài đặt Java 7. Có thể có một số lỗi.

2

Bạn có thể làm cho mã của mình đẹp hơn một chút, nhưng vào cuối ngày, mã sẽ vẫn trông giống như mẫu khách truy cập cũ đơn giản.

2

Dưới đây là Daniel của kịch bản đã compilable:

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

Lấy Daniel's answer như tảng, tôi đã làm việc một chút để làm Path tiếp cận với implicits thuận tiện, như bạn đang sử dụng trong bộ sưu tập. Lưu ý rằng không phải tất cả các hàm đều được bao gồm.

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) 

API java.nio cực kỳ mạnh mẽ và, IMHO, rất sufficing để sử dụng với Scala. Với những hàm ý này (và nhiều hơn nữa, nếu bạn muốn viết một số hàm), nó rất đơn giản để hoàn thành các nhiệm vụ khó khăn hơn.

Bạn có thể sử dụng này ngay bây giờ bằng cách viết một cái gì đó như thế này:

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 

Kính trọng,
Danyel

0

FIles.walkFileTree ví dụ để so sánh hai thư mục/đồng bộ hóa hai thư mục cho sự khác biệt tập tin

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

Mở rộng ý tưởng của các bài đăng khác. Tôi thích giải pháp mà chúng tôi có thể khớp với các lớp chữ thường. Đoạn mã sau chỉ trả về một tập hợp các chuỗi cho các sự kiện khác nhau mà khách truy cập đã được gọi.

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

Việc thực hiện FileWalker là:

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) 
} 
Các vấn đề liên quan