2013-01-25 51 views
9

我正在集成测试我的代码的一部分,它在SVN下创建目录树。这要求我测试目录结构和内部文件是否我期望它们是。测试两个目录树是否相等

一方面我有我想要的文件,并在其他预期的目录树中的文件,从SVN出口(喜欢svn export超过svn co避免.svn噪声)。

但是,是否有任何库可以断言两个目录树?我想到的最后一招是自己做一个迭代比较。

基本上我正在寻找一个API,可以接受两个目录,并告诉我他们是否相等。对

boolean areDirectoriesEqual(File dir1, File dir2) 
+0

因此,在总结,你有文件的两个目录结构,你想这两种结构放在一起比较? –

+0

是的。那就对了。现在,我甚至不在乎它们之间的差异。我只需要知道他们是否平等。 – adarshr

+1

如果您使用的是Java 7,也许可以创建两个[FileVisitors](http://docs.oracle.com/javase/7/docs/api/java/nio/file/FileVisitor.html),然后走树? – hertzsprung

回答

-5
import java.io.File; 

/** 
* 
* FileUtils is a collection of routines for common file system operations. 
* 
* @author Dan Jemiolo (danj) 
* 
*/ 

public final class FileUtils { 

    /** 
    * 
    * This is a convenience method that calls find(File, String, boolean) with 
    * the last parameter set to "false" (does not match directories). 
    * 
    * @see #find(File, String, boolean) 
    * 
    */ 
    public static File find(File contextRoot, String fileName) { 
    return find(contextRoot, fileName, false); 
    } 

    /** 
    * 
    * Searches through the directory tree under the given context directory and 
    * finds the first file that matches the file name. If the third parameter is 
    * true, the method will also try to match directories, not just "regular" 
    * files. 
    * 
    * @param contextRoot 
    *   The directory to start the search from. 
    * 
    * @param fileName 
    *   The name of the file (or directory) to search for. 
    * 
    * @param matchDirectories 
    *   True if the method should try and match the name against directory 
    *   names, not just file names. 
    * 
    * @return The java.io.File representing the <em>first</em> file or 
    *   directory with the given name, or null if it was not found. 
    * 
    */ 
    public static File find(File contextRoot, String fileName, boolean matchDirectories) { 
    if (contextRoot == null) 
     throw new NullPointerException("NullContextRoot"); 

    if (fileName == null) 
     throw new NullPointerException("NullFileName"); 

    if (!contextRoot.isDirectory()) { 
     Object[] filler = { contextRoot.getAbsolutePath() }; 
     String message = "NotDirectory"; 
     throw new IllegalArgumentException(message); 
    } 

    File[] files = contextRoot.listFiles(); 

    // 
    // for all children of the current directory... 
    // 
    for (int n = 0; n < files.length; ++n) { 
     String nextName = files[n].getName(); 

     // 
     // if we find a directory, there are two possibilities: 
     // 
     // 1. the names match, AND we are told to match directories. 
     // in this case we're done 
     // 
     // 2. not told to match directories, so recurse 
     // 
     if (files[n].isDirectory()) { 
     if (nextName.equals(fileName) && matchDirectories) 
      return files[n]; 

     File match = find(files[n], fileName); 

     if (match != null) 
      return match; 
     } 

     // 
     // in the case of regular files, just check the names 
     // 
     else if (nextName.equals(fileName)) 
     return files[n]; 
    } 

    return null; 
    } 

} 
+0

对不起,我不认为你理解我的问题。做一个'find'并不是我想要做的。 – adarshr

+1

我不在这里搜索目录中的文件。我只想要一个像'boolean areDirsEqual(File dir1,File dir2)'这样的方法。 – adarshr

+0

请参阅您首先将所有文件存储在2个目录的文件匹配项中。然后你可以比较两场比赛 –

1

OK线

东西,所以我不知道任何现成的一段代码,这是否和搜索没有帮助的。因此,这里是我将如何实现它

  1. 迭代递归上的所有文件夹和文件
  2. 从根保存相对路径所有文件名中的一个HashSet,其中相对路径是键/值
  3. 迭代递归地在第二个目录结构上创建一个从每个路径匹配hashet中的键(如果文件夹/文件存在)

如果您只是想将树标记为已更改/未更改,则可以保存每个文件的散列,那么你需要一个hashmap而不是hashset,其中t他的每一个文件的内容哈希散列映射的值

希望这有助于

+0

是的,算法没问题。但我主要是在寻找能够完成所有这些工作的第三方库。 – adarshr

+0

是的我明白你的问题,但据说我找不到任何东西:(所以.. –

2

我不知道任何areDirsEqual库;我能想到的最接近的是Commons FileUtils中的listFiles方法。

如果您将得到的集合放在HashSet中,您应该能够有效地比较这两个集合。它可以在两行中完成,甚至可以是一行代码。

东西在这条线:

public static boolean areDirsEqual(File dir, File dir2) { 
    return (new HashSet<File>(FileUtils.listFiles(dir1,..))). 
      containsAll(FileUtils.listFiles(dir2, ..)) 
} 
+1

是的,这是验证结构的一种方法,我想现在它会做,直到我能找到/写一个综合实用程序甚至可以处理差异并突出显示它们,这对于在github上启动一个新项目实际上是一个好主意! – adarshr

+0

由于绝对路径名用于比较,所以这不起作用,它仅返回true,如果目录是相同的(在这种情况下,你当然可以比较根目录) – simon

7
我没有使用第三方的lib,但标准的JDK的lib

private static void verifyDirsAreEqual(Path one, Path other) throws IOException { 
    Files.walkFileTree(one, new SimpleFileVisitor<Path>() { 
     @Override 
     public FileVisitResult visitFile(Path file, 
       BasicFileAttributes attrs) 
       throws IOException { 
      FileVisitResult result = super.visitFile(file, attrs); 

      // get the relative file name from path "one" 
      Path relativize = one.relativize(file); 
      // construct the path for the counterpart file in "other" 
      Path fileInOther = other.resolve(relativize); 
      log.debug("=== comparing: {} to {}", file, fileInOther); 

      byte[] otherBytes = Files.readAllBytes(fileInOther); 
      byte[] thisBytes = Files.readAllBytes(file); 
      if (!Arrays.equals(otherBytes, thisTypes)) { 
       throw new AssertionFailedError(file + " is not equal to " + fileInOther); 
      } 
      return result; 
     } 
    }); 
} 

注意:这仅仅是在比较两个文件夹的实际文件。如果你有空的文件夹等你想比较,你可能需要做一些额外的事情。

+0

你的答案的第一行是不正确的,你**正在使用一些库来做比较文件的实际工作,如果你能用更精确的信息来更新你的答案,那么这将是一件好事,否则,这是一个很好的答案。 – Torque

+0

已更新。来自junit。更改为使用任何其他异常类型(如果不适用)。 – Patrick

3

我有同样的问题,并跟随帕特里克和洛伦佐Dematté我找到了一个适合我的解决方案。下面的代码遍历文件夹:

  • 每个子文件夹检查文件列表是同一
  • 每个文件的内容进行比较(在我的情况我比较包含CSV文件的两个文件夹)

我在linux上测试过它。

private static void verifyDirsAreEqual(File expected, File generated) 
       throws IOException { 

    // Checks parameters 
    assertTrue("Generated Folder doesn't exist: " + generated,generated.exists()); 
    assertTrue("Generated is not a folder?!?!: " + generated,generated.isDirectory()); 

    assertTrue("Expected Folder doesn't exist: " + expected,expected.exists()); 
    assertTrue("Expected is not a folder?!?!: " + expected,expected.isDirectory());  

    Files.walkFileTree(expected.toPath(), new SimpleFileVisitor<Path>() { 
     @Override 
     public FileVisitResult preVisitDirectory(Path dir, 
       BasicFileAttributes attrs) 
        throws IOException { 
      FileVisitResult result = super.preVisitDirectory(dir, attrs); 

      // get the relative file name from path "expected" 
      Path relativize = expected.toPath().relativize(dir); 
      // construct the path for the counterpart file in "generated" 
      File otherDir = generated.toPath().resolve(relativize).toFile(); 
      log.debug("=== preVisitDirectory === compare " + dir + " to " + otherDir); 
      assertEquals("Folders doesn't contain same file!?!?", 
        Arrays.toString(dir.toFile().list()), 
        Arrays.toString(otherDir.list())); 
      return result; 
     } 
     @Override 
     public FileVisitResult visitFile(Path file, 
       BasicFileAttributes attrs) 
       throws IOException { 
      FileVisitResult result = super.visitFile(file, attrs); 

      // get the relative file name from path "expected" 
      Path relativize = expected.toPath().relativize(file); 
      // construct the path for the counterpart file in "generated" 
      File fileInOther = generated.toPath().resolve(relativize).toFile(); 
      log.debug("=== comparing: " + file + " to " + fileInOther); 
      String expectedContents = FileUtils.readFileToString(file.toFile()); 
      String generatedContents = FileUtils.readFileToString(fileInOther); 
      assertEquals("("+fileInOther+") csv standard doesn't match expected ("+file+")!", expectedContents, generatedContents);      
      return result; 
     } 
    }); 
} 
0

这是使用Java NIO包简单迭代溶液(不使用访问者模式,因此它可以适于较早的Java版本以及)。

当然可以调整,但现在这是一个简单的解决方案,从两个目录的视图中检查每个文件是否出现,并可选择使用Apache Commons FileUtils比较文件内容。

/** 
* checks if the directory file lists and file content is equal 
* 
* @param directory 
*   the directory 
* @param compareDirectory 
*   the directory to compare with 
* @param checkFileContent 
*   also compare file content 
* @return true if directory and compareDirectory are equal 
* @throws IOException 
*/ 
public static boolean isEqualDirectories(Path directory, Path compareDirectory, boolean checkFileContent) throws IOException { 
    boolean check = isEverythingInCompareDirectory(directory, compareDirectory, checkFileContent); 
    boolean checkOpposite = check && isEverythingInCompareDirectory(directory, compareDirectory, checkFileContent); 
    return check && checkOpposite; 

} 

/** 
* checks if the directory file lists and file content is equal 
* 
* @param directory 
*   the directory 
* @param compareDirectory 
*   the directory to compare with 
* @param checkFileContent 
*   also compare file content 
* @return true if directory and compareDirectory are equal 
* @throws IOException 
*/ 
public static boolean isEverythingInCompareDirectory(Path directory, Path compareDirectory, boolean checkFileContent) 
     throws IOException { 

    try { 
     LOGGER.info("checking directory " + directory); 

     File directoryFile = directory.toFile(); 
     File compareFile = compareDirectory.toFile(); 

     // check, if there is the same number of files/subdirectories 
     File[] directoryFiles = directoryFile.listFiles(); 
     File[] compareFiles = compareFile.listFiles(); 

     if (directoryFiles.length == compareFiles.length) { 
      return compareDirectoryContents(directory, compareDirectory, checkFileContent); 

     } else { 
      LOGGER.info("number of files in directory are different " + directoryFiles.length + " vs compareDirectory: " + compareFiles.length); 
      return false; 
     } 

    } catch (IOException e) { 
     throw new RuntimeException("Failed to assert that all files are equal", e); 
    } 
} 

public static boolean compareDirectoryContents(Path directory, Path compareDirectory, boolean checkFileContent) throws IOException { 
    try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) { 

     for (Path directoryFilePath : directoryStream) { 

      // search for directoryFile in the compareDirectory 
      Path compareFilePath = compareDirectory.resolve(directoryFilePath.getFileName()); 

      if (compareFilePath != null) { 

       File directoryFile = directoryFilePath.toFile(); 
       if (directoryFile.isFile()) { 
        LOGGER.info("checking file " + directoryFilePath); 
        if (checkFileContent && !FileUtils.contentEquals(compareFilePath.toFile(), directoryFile)) { 
         LOGGER.info("files not equal: compare: " + compareFilePath.toFile() + ", directory: " + directoryFilePath.getFileName() + "!"); 
         return false; 
        } 

       } else { 
        LOGGER.info("going into recursion with directory " + directoryFilePath); 
        boolean result = isEverythingInCompareDirectory(directoryFilePath, compareFilePath, checkFileContent); 
        // cancel if not equal, otherwise continue processing 
        if (!result) { 
         return false; 
        } 

       } 
      } else { 
       LOGGER.info(directoryFilePath.toString() + ": compareFilepath not found"); 
       return false; 
      } 

     } 
    } 

    return true; 
} 
0

我在Kotlin写了这个小代码。它不检查文件的内容,但完全依赖apache的md5。

import org.apache.commons.codec.digest.DigestUtils 

fun File.calcMD5() = DigestUtils.md5Hex(FileUtils.readFileToByteArray(this)) 

fun compareTwoDirs(dir1: File, dir2: File): Boolean { 
    val files1 = dir1.listFiles().sorted() 
    val files2 = dir2.listFiles().sorted() 
    if (files1.size != files2.size) return false 
    return files1.zip(files2).all { equate(it.first, it.second) } 
} 

fun equate(fl: File, fl2: File): Boolean { 
    if (fl.isFile && fl2.isFile) return fl.calcMD5() == fl2.calcMD5() 
    if (fl.isDirectory && fl2.isDirectory) return compareTwoDirs(fl, fl2) 
    return false 
} 
0

我帕特里克的解决方案似乎是一个很好的解决方案,但与骆驼(保险丝ESB)组合我有最后的父文件夹仍然封锁保险丝过程=>我的后续问题解决方案是更好的方法。我遍历目录疗法通过SimpleVistor,并提出了一套类似的展台目录

public boolean compareFolders(final Path pathOne, final Path pathSecond) throws IOException { 

    // get content of first directory 
    final TreeSet<String> treeOne = new TreeSet(); 
    Files.walkFileTree(pathOne, new SimpleFileVisitor<Path>() { 
     @Override 
     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 
      Path relPath = pathOne.relativize(file); 
      String entry = relPath.toString(); 
      treeOne.add(entry); 
      return FileVisitResult.CONTINUE; 
     } 
    }); 

    // get content of second directory 
    final TreeSet<String> treeSecond = new TreeSet(); 
    Files.walkFileTree(pathSecond, new SimpleFileVisitor<Path>() { 
     @Override 
     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 
      Path relPath = pathSecond.relativize(file); 
      String entry = relPath.toString(); 
      treeSecond.add(entry); 
      return FileVisitResult.CONTINUE; 
     } 
    }); 
    return treeOne.equals(treeSecond); 
}