2013-01-25 11 views
9

Sto lavorando sull'integrazione testando parte del mio codice che crea alberi di directory sotto SVN. Ciò mi richiede di verificare se la struttura della directory e i file all'interno sono ciò che mi aspetto che siano.Test di due alberi di directory per l'uguaglianza

Da una parte ho l'albero delle directory atteso con i file che voglio e, dall'altro, l'esportazione dei file da SVN (preferiscono svn export sopra svn co per evitare il rumore .svn).

Tuttavia, esiste una libreria in grado di affermare due alberi di directory? L'ultima risorsa che ho in mente è di fare un confronto iterativo me stesso.

Fondamentalmente sto cercando un'API che può solo accettare due directory e dirmi se sono uguali o meno.

Qualcosa sulla falsariga di

boolean areDirectoriesEqual(File dir1, File dir2) 
+0

Quindi, in sintesi, si hanno due strutture di directory con i file e si desidera confrontare insieme le due strutture? –

+0

Sì. Giusto. Per ora, non mi interessa nemmeno quali siano le differenze. Devo solo sapere se sono uguali o meno. – adarshr

+1

Se stai utilizzando Java 7, potresti creare due [FileVisitors] (http://docs.oracle.com/javase/7/docs/api/java/nio/file/FileVisitor.html) e camminare sugli alberi ? – hertzsprung

risposta

-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

Scusa, non penso che tu abbia capito la mia domanda. Fare un 'find' non è quello che voglio fare. – adarshr

+1

Non sto cercando i file all'interno di una directory qui. Tutto quello che voglio è un metodo come 'boolean areDirsEqual (File dir1, File dir2)'. – adarshr

+0

Vedere, per prima cosa memorizzare tutti i file nella corrispondenza del file per le 2 directory. Quindi puoi confrontare le due corrispondenze –

1

OK quindi non so di qualsiasi pezzo pronto di codice che fa questo e ricerca non ha aiutato neanche. Quindi, ecco come avrei implementarlo

  1. iterata in modo ricorsivo a tutte le cartelle ei file
  2. salvare tutti i nomi di file con percorso relativo dalla radice in un HashSet, in cui il percorso relativo è la chiave/valore
  3. iterate ricorsivamente sulla seconda struttura di directory e creare una da ogni percorso per abbinare le chiavi nella hashet (se esiste la cartella/file)

Se si desidera solo per marcare l'albero come modificato/non è cambiato, è possibile salvare l'hash di ogni file, quindi hai bisogno di una hashmap invece di hashset, dove t egli hash del contenuto di ciascun file è il valore della HashMap

speranza che questo aiuta

+0

Sì, l'algoritmo va bene. Ma cercavo principalmente una libreria di terze parti che facesse tutto questo. – adarshr

+0

sì, ho capito la tua domanda, ma come ho detto non sono riuscito a trovare nulla :(, quindi .. –

2

io non sono a conoscenza di qualsiasi libreria areDirsEqual; il più vicino a cui riesco a pensare è il metodo listFiles in Commons FileUtils.

Se si inseriscono le raccolte risultanti in un HashSet, si dovrebbe essere in grado di confrontare i due set in modo efficiente. E può essere fatto in 2 linee, forse anche in un solo liner.

Qualcosa su questa linea:

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

Sì. Questo è un modo per verificare la struttura. Suppongo che lo farà per ora fino a quando non riesco a trovare/scrivere un Un'utility completa che può anche gestire i diff e metterli in evidenza: è una buona idea iniziare un nuovo progetto su github! – adarshr

+0

Questo non funziona confrontando le strutture di directory, poiché i nomi assoluti vengono usati per il confronto. Restituisce vero solo se le directory sono identici (e in tal caso, puoi ovviamente confrontare solo la directory root) – simon

7

non sto usando una terza parte lib ma lib JDK standard.

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

Nota: si tratta solo di confrontare i file effettivi in ​​due cartelle. Se hai vuote cartelle, vuoi confrontarle, potresti dover fare qualcosa in più.

+0

La prima riga della tua risposta non è corretta. ** Stai ** usando qualche libreria per fare il vero lavoro di confronto del file, andando fuori da Google, indovinerei AspectJ.Sarebbe bene se si potesse aggiornare la risposta con informazioni più precise, perché altrimenti è una buona risposta – Torque

+0

Aggiornato. Solo l'eccezione è da junit. Cambiare per utilizzare qualsiasi altro tipo di eccezione se non è adatto. – Patrick

3

Ho avuto lo stesso problema e seguendo Patrick e Lorenzo Dematté ho trovato una soluzione che funziona per me.Il seguente codice cammina attraverso la cartella e:

  • per ogni controlli sottocartelle se elenchi di file sono gli stessi
  • per ogni file confronta il contenuto (nel mio caso devo confrontare due cartelle che contengono file csv)

L'ho provato su 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

Si tratta di una soluzione iterativa semplice utilizzando il pacchetto Java NIO (senza utilizzare il modello visitatore, in modo che possa essere adattato per le versioni precedenti di Java pure).

Ovviamente potrebbe essere regolato, ma per ora questa è una soluzione semplice che controlla dalla vista di entrambe le directory se si verifica ogni file e opzionalmente confronta il contenuto del file usando i FileUtils di Apache Commons.

/** 
* 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

Ho scritto questo piccolo codice in Kotlin. Non controlla il contenuto dei file, ma fa affidamento completamente su md5 da 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 
} 
0

per me la soluzione di Patrick sembra essere una buona soluzione, ma in combinazione con cammello (FUSE ESB) ho avuto il problema che l'ultima cartella principale era ancora bloccato da processo fusibile => per me il follow le soluzioni semms per essere il modo migliore. I iterare su ther directory di SimpleVistor e ha fatto un Set comparabile di directory stand

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); 
} 
Problemi correlati