18

我正在对我的代码的某些部分进行集成测试,这些代码在 SVN 下创建目录树。这需要我测试目录结构和其中的文件是否符合我的预期。

一方面,我有我想要的文件的预期目录树,另一方面,从 SVN 导出文件(宁愿svn export避免svn co噪音.svn)。

但是,有没有可以断言两个目录树的库?我想到的最后手段是自己进行迭代比较。

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

有点意思

boolean areDirectoriesEqual(File dir1, File dir2)
4

9 回答 9

11

我没有使用第三方库,而是使用标准 jdk 库。

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[] theseBytes = Files.readAllBytes(file);
            if (!Arrays.equals(otherBytes, theseBytes)) {
                throw new AssertionFailedError(file + " is not equal to " + fileInOther);
            }  
            return result;
        }
    });
}

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

于 2016-09-20T01:05:14.657 回答
3

我不知道有任何areDirsEqual图书馆;我能想到的最接近的是 Commons 中的 listFiles 方法FileUtils

如果将结果集合放在 a 中HashSet,您应该能够有效地比较这两个集合。它可以在 2 行中完成,甚至可以是单行。

这条线上的东西:

public static boolean areDirsEqual(File dir, File dir2) {
  return (new HashSet<File>(FileUtils.listFiles(dir1,..))).
          containsAll(FileUtils.listFiles(dir2, ..))
}
于 2013-01-25T13:27:59.460 回答
3

我遇到了同样的问题,跟随 Patrick 和 Lorenzo 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;
        }
    });
}
于 2016-11-24T13:35:11.213 回答
2

对我来说,Patrick 的解决方案似乎是一个很好的解决方案,但结合骆驼(Fuse ESB)我遇到了最后一个父文件夹仍然被 Fuse 进程阻止的问题 => 对我来说,以下解决方案似乎是更好的方法。我通过 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);
}
于 2018-03-01T17:56:50.597 回答
1

好的,所以我不知道有任何现成的代码可以做到这一点,搜索也没有帮助。所以这就是我将如何实现它

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

如果只想将树标记为已更改/未更改,可以保存每个文件的hash,那么需要一个hashmap而不是hashset,其中每个文件内容的hash就是hashmap的值

希望这可以帮助

于 2013-01-25T13:16:46.007 回答
1

这是一个使用 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;
}
于 2017-02-12T16:02:37.457 回答
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
}
于 2017-11-26T14:38:54.837 回答
0

我知道这是一个老问题,但我会尝试解决它。这是一个打开 2 个目录并比较它们的内容的代码。如果在一个文件中找到的文件在另一个文件中丢失,或者它们的内容不同,则返回 false。为此,我浏览了 2 个目录的内容,并递归地比较找到的文件和目录(按字母顺序)。

// Comparison of 2 files
private static boolean compareFiles(File f1, File f2) {
    long i1 = f1.length();
    long i2 = f2.length();
    if (i1 != i2) {
        return false;
    }
    try {
        byte b1, b2;
        DataInputStream dis = new DataInputStream(
                new BufferedInputStream(
                        new FileInputStream(f1)));
        DataInputStream dis1 = new DataInputStream(
                new BufferedInputStream(
                        new FileInputStream(f2)));
        while (true) {
            b1 = dis.readByte();
            b2 = dis1.readByte();
            if (b1 != b2) {
                return false;
            }
        }
    } catch (IOException ex) {
        return true;
    }
}


// Recursive comparison of 2 files.
// The idea is that if 2 directories are the same, 
// they will have the same files and the same under directories
private static boolean areDirsEqual(File dir1, File dir2) {
    File[] list1 = dir1.listFiles(), list2 = dir2.listFiles();
    if (list1.length != list2.length) {
        return false;
    }
    for (int i = 0; i < list2.length; i++) {
        if (list1[i].isFile() && list2[i].isFile()) {
            if (!compareFiles(list1[i], list2[i])) {
                return false;
            }
        } else if (list1[i].isDirectory() && list2[i].isDirectory()) {
            if (!areDirsEqual(list1[i], list2[i])) {
                return false;
            }
        } else {
            return false;
        }
    }
    return true;
}
于 2019-05-29T23:00:25.257 回答
-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;
  }

}
于 2013-01-25T13:00:00.183 回答