Tester l’égalité de deux arbres de répertoires

Je travaille actuellement sur l’intégration d’une partie de mon code qui teste la création d’arborescences de répertoires sous SVN. Cela nécessite que je vérifie si la structure des répertoires et les fichiers qu’ils contiennent sont ce que j’attends d’eux.

D’un côté, j’ai l’arborescence de répertoires attendue avec les fichiers que je veux et de l’autre côté, une exportation des fichiers de SVN (préférez svn export à svn co pour éviter le bruit .svn ).

Cependant, existe-t-il une bibliothèque capable d’affirmer deux arborescences de répertoires? Le dernier recours auquel je pense est de faire moi-même une comparaison itérative.

En gros, je recherche une API capable d’accepter deux répertoires et de me dire s’ils sont égaux ou non.

Quelque chose sur les lignes de

 boolean areDirectoriesEqual(File dir1, File dir2) 

Je n’utilise pas une bibliothèque tierce mais la bibliothèque standard jdk.

 private static void verifyDirsAreEqual(Path one, Path other) throws IOException { Files.walkFileTree(one, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAtsortingbutes 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; } }); } 

Remarque: il s’agit simplement de comparer des fichiers réels dans deux dossiers. Si vous souhaitez également comparer des dossiers vides, etc., vous devrez peut-être effectuer certaines tâches supplémentaires.

J’ai eu le même problème et après Pasortingck et Lorenzo Dematté, j’ai trouvé une solution qui fonctionne pour moi. Le code suivant parcourt le dossier et:

  • pour chaque sous-dossier vérifie si les listes de fichiers sont les mêmes
  • pour chaque fichier compare le contenu (dans mon cas, je dois comparer deux dossiers contenant des fichiers CSV)

Je l’ai testé sur 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() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAtsortingbutes 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.toSsortingng(dir.toFile().list()), Arrays.toSsortingng(otherDir.list())); return result; } @Override public FileVisitResult visitFile(Path file, BasicFileAtsortingbutes 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); Ssortingng expectedContents = FileUtils.readFileToSsortingng(file.toFile()); Ssortingng generatedContents = FileUtils.readFileToSsortingng(fileInOther); assertEquals("("+fileInOther+") csv standard doesn't match expected ("+file+")!", expectedContents, generatedContents); return result; } }); } 

Je ne connais aucune bibliothèque areDirsEqual ; le plus proche auquel je puisse penser est la méthode listFiles dans Commons FileUtils .

Si vous placez les collections résultantes dans un HashSet , vous devriez pouvoir comparer efficacement les deux ensembles. Et cela peut être fait en 2 lignes, peut-être même en une ligne.

Quelque chose sur cette ligne:

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

OK, je ne connais donc pas de code prêt à l’emploi qui effectue cela, et la recherche n’a pas non plus aidé. Alors voici comment je le mettrais en œuvre

  1. itérer récursivement sur tous les dossiers et fichiers
  2. enregistrer tous les noms de fichiers avec le chemin relatif depuis la racine dans un hachage, où le chemin relatif est la clé / la valeur
  3. itérer récursivement sur la deuxième structure de répertoire et créer un depuis chaque chemin correspondant aux clés du hashet (si le dossier / fichier existe)

Si vous souhaitez simplement marquer l’arborescence comme modifiée / non modifiée, vous pouvez enregistrer le hachage de chaque fichier. Vous aurez alors besoin d’une hashmap au lieu de hashset, où le hachage du contenu de chaque fichier est la valeur de la hashmap.

J’espère que cela t’aides

Il s’agit d’une solution simple et itérative utilisant le package Java NIO (sans utiliser le modèle Visitor, elle peut donc également être adaptée aux versions Java antérieures).

Bien sûr, il peut être réglé, mais pour l’instant, il s’agit d’une solution simple. Dans chaque fichier, il est possible de vérifier à partir de la vue des deux répertoires et de comparer le contenu du fichier à l’aide de 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 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.toSsortingng() + ": compareFilepath not found"); return false; } } } return true; } 

J’ai écrit ce petit code dans Kotlin. Il ne vérifie pas le contenu des fichiers, mais repose complètement sur md5 d’apache.

 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 } 

Pour moi, la solution de Pasortingck semble être une bonne solution, mais en combinaison avec camel (Fuse ESB), j’ai eu le problème que le dernier dossier parent était toujours bloqué par le processus Fuse => pour moi, les solutions suivantes semblaient être un meilleur moyen. J’ai parcouru les répertoires de SimpleVistor et créé un ensemble comparable de répertoires de stands

 public boolean compareFolders(final Path pathOne, final Path pathSecond) throws IOException { // get content of first directory final TreeSet treeOne = new TreeSet(); Files.walkFileTree(pathOne, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAtsortingbutes attrs) throws IOException { Path relPath = pathOne.relativize(file); Ssortingng entry = relPath.toSsortingng(); treeOne.add(entry); return FileVisitResult.CONTINUE; } }); // get content of second directory final TreeSet treeSecond = new TreeSet(); Files.walkFileTree(pathSecond, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAtsortingbutes attrs) throws IOException { Path relPath = pathSecond.relativize(file); Ssortingng entry = relPath.toSsortingng(); treeSecond.add(entry); return FileVisitResult.CONTINUE; } }); return treeOne.equals(treeSecond); } 
 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, Ssortingng, boolean) with * the last parameter set to "false" (does not match directories). * * @see #find(File, Ssortingng, boolean) * */ public static File find(File contextRoot, Ssortingng 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 first file or * directory with the given name, or null if it was not found. * */ public static File find(File contextRoot, Ssortingng fileName, boolean matchDirectories) { if (contextRoot == null) throw new NullPointerException("NullContextRoot"); if (fileName == null) throw new NullPointerException("NullFileName"); if (!contextRoot.isDirectory()) { Object[] filler = { contextRoot.getAbsolutePath() }; Ssortingng 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; } }