2009-09-03 8 views
7

Aquí está mi objetivo. Quiero poder pasar un directorio principal y un nombre de archivo a un método que busque ese archivo específico en el directorio y en cualquier subdirectorio. Debajo está el código con el que he estado trabajando pero no puedo hacer que haga exactamente lo que quiero. Encontrará el archivo que especifico pero no devolverá nada.Buscar un archivo en el directorio con múltiples directorios

private static File findFile(File dir, String name) { 
    String file  = ""; 
    File[] dirlist = dir.listFiles(); 

    search: 
     for(int i = 0; i < dirlist.length; i++) { 
      if(dirlist[i].isDirectory()) { 
       findFile(dirlist[i], name); 
      } else if(dirlist[i].getName().matches(name)) { 
       file = dirlist[i].toString(); 
       break search; 
      } 
     } 

    return new File(file); 
} 

sé que cuando el método encuentra un directorio y llama a sí mismo se restablece la variable de fichero que es donde estoy almacenando el archivo encontrado. Entonces, es por eso que recibo un retorno en blanco. No estoy seguro de cómo lograr este objetivo o si es posible.

+0

Aparte: puede/debería usar un "corte" regular en lugar de "romper la etiqueta" aquí. (Un retorno simple es mejor aún como señala @ chssPly76.) –

Respuesta

5

El problema es que no se está volviendo algo de la llamada recursiva:

if(dirlist[i].isDirectory()) { 
    findFile(dirlist[i], name); // <-- here 
} else if(dirlist[i].getName().matches(name)) { 

yo haría lo siguiente:

private static File findFile(File dir, String name) { 
    File result = null; // no need to store result as String, you're returning File anyway 
    File[] dirlist = dir.listFiles(); 

    for(int i = 0; i < dirlist.length; i++) { 
    if(dirlist[i].isDirectory()) { 
     result = findFile(dirlist[i], name); 
     if (result!=null) break; // recursive call found the file; terminate the loop 
    } else if(dirlist[i].getName().matches(name)) { 
     return dirlist[i]; // found the file; return it 
    } 
    } 
    return result; // will return null if we didn't find anything 
} 
+0

Esto no funciona si el archivo no existe en el directorio y generará una excepción – Erfan

1

De hecho, hay muchas soluciones para hacer el trabajo. Supongo que desea encontrar un archivo único (o el primero) que se encuentra en un árbol de directorios que coincide con el nombre de archivo. Es un problema de optimización porque hay varias formas de explorar soluciones y queremos encontrar una solución aceptable.

1- solución utilizando FileUtils.listFiles

public static File searchFileWithFileUtils(final File file, final String fileName) { 
    File target = null; 
    if(file.isDirectory()) { 
     Collection<File> files = FileUtils.listFiles(file, null, true); 
     for (File currFile : files) { 
      if (currFile.isFile() && currFile.getName().equals(fileName)) { 
       target = currFile; 
       break; 
      } 
     } 
    } 
    return target; 
} 

la solución usando la biblioteca FileUtils no es una solución adecuada porque el método FileUtils#listFiles() cargas de todo el árbol de directorios/carpetas (el costo es caro!). No necesitamos saber todo el árbol, podemos elegir un mejor algoritmo que se detiene cuando se encuentra el archivo.

2- recursiva Solución

public static File searchFileRecursive(final File file, final String search) { 
    if (file.isDirectory()) { 
     File[] files = file.listFiles(); 
     for (File f : files) { 
      File target = searchFileRecursive(f, search); 
      if(target != null) { 
       return target; 
      } 
     } 
    } else { 
     if (search.equals(file.getName())) { 
      return file; 
     } 
    } 
    return null; 
} 

Las pruebas del algoritmo si existe el archivo dentro de ninguna carpeta. Si no, intenta subcarpeta de la carpeta actual ... recursivamente. Si el archivo no se encuentra en la rama actual, intenta con otra subcarpeta.

La exploración es profunda, y para cualquier archivo con una profundidad de 1, el algoritmo explorará la totalidad de las subcarpetas anteriores (¡las ramas anteriores se exploraron por completo!). Este algoritmo tiene el mejor rendimiento para archivos en una ubicación profunda dentro de la primera rama.

En la mayoría de los casos, la ubicación del archivo no es profunda, así que deja explorar otro algoritmo que funciona en la mayoría de los casos.

3- rápido Solución: la exploración de profundidad

public static File searchFileByDeepness(final String directoryName, final String fileName) { 
    File target = null; 
    if(directoryName != null && fileName != null) { 
     File directory = new File(directoryName); 
     if(directory.isDirectory()) { 
      File file = new File(directoryName, fileName); 
      if(file.isFile()) { 
       target = file; 
      } 
      else { 
       List<File> subDirectories = getSubDirectories(directory); 
       do { 
        List<File> subSubDirectories = new ArrayList<File>(); 
        for(File subDirectory : subDirectories) { 
         File fileInSubDirectory = new File(subDirectory, fileName); 
         if(fileInSubDirectory.isFile()) { 
          return fileInSubDirectory; 
         } 
         subSubDirectories.addAll(getSubDirectories(subDirectory)); 
        } 
        subDirectories = subSubDirectories; 
       } while(subDirectories != null && ! subDirectories.isEmpty()); 
      } 
     } 
    } 
    return target; 
} 

private static List<File> getSubDirectories(final File directory) { 
    File[] subDirectories = directory.listFiles(new FilenameFilter() { 
     @Override 
     public boolean accept(final File current, final String name) { 
      return new File(current, name).isDirectory(); 
     } 
    }); 
    return Arrays.asList(subDirectories); 
} 

Para cada profundidad, el algoritmo busca en el archivo dentro de todas las carpetas del mismo nivel. Si el archivo no se encuentra, intenta el siguiente nivel (profundidad ++). Debido a la exploración en paralelo (simetría), esta solución es adecuada en la mayoría de los casos.

comparación:

public class FileLocationFinder { 

    public static void main(final String[] args) { 
     String rootFolder = args[0]; 
     String fileName = args[1]; 

     long start = System.currentTimeMillis(); 
     File target = searchFileWithFileUtils(new File(rootFolder), fileName); 
     System.out.println(target.getAbsolutePath()); 
     System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms"); 

     start = System.currentTimeMillis(); 
     target = searchFileRecursive(new File(rootFolder), fileName); 
     System.out.println(target.getAbsolutePath()); 
     System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms"); 

     start = System.currentTimeMillis(); 
     target = searchFileByDeepness(rootFolder, fileName); 
     System.out.println(target.getAbsolutePath()); 
     System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms"); 
    } 


    // Solution with FileUtils#listFiles 
    //-------------------------------------------- 

    public static File searchFileWithFileUtils(final File file, final String fileName) { 
     File target = null; 
     if(file.isDirectory()) { 
      Collection<File> files = FileUtils.listFiles(file, null, true); 
      for (File currFile : files) { 
       if (currFile.isFile() && currFile.getName().equals(fileName)) { 
        target = currFile; 
        break; 
       } 
      } 
     } 
     return target; 
    } 


    // Recursive solution 
    //-------------------------------------------- 

    public static File searchFileRecursive(final File file, final String search) { 
     if (file.isDirectory()) { 
      File[] files = file.listFiles(); 
      for (File f : files) { 
       File target = searchFileRecursive(f, search); 
       if(target != null) { 
        return target; 
       } 
      } 
     } else { 
      if (search.equals(file.getName())) { 
       return file; 
      } 
     } 
     return null; 
    } 


    // Fastest solution 
    //-------------------------------------------- 

    public static File searchFileByDeepness(final String directoryName, final String fileName) { 
     File target = null; 
     if(directoryName != null && fileName != null) { 
      File directory = new File(directoryName); 
      if(directory.isDirectory()) { 
       File file = new File(directoryName, fileName); 
       if(file.isFile()) { 
        target = file; 
       } 
       else { 
        List<File> subDirectories = getSubDirectories(directory); 
        do { 
         List<File> subSubDirectories = new ArrayList<File>(); 
         for(File subDirectory : subDirectories) { 
          File fileInSubDirectory = new File(subDirectory, fileName); 
          if(fileInSubDirectory.isFile()) { 
           return fileInSubDirectory; 
          } 
          subSubDirectories.addAll(getSubDirectories(subDirectory)); 
         } 
         subDirectories = subSubDirectories; 
        } while(subDirectories != null && ! subDirectories.isEmpty()); 
       } 
      } 
     } 
     return target; 
    } 

    private static List<File> getSubDirectories(final File directory) { 
     File[] subDirectories = directory.listFiles(new FilenameFilter() { 
      @Override 
      public boolean accept(final File current, final String name) { 
       return new File(current, name).isDirectory(); 
      } 
     }); 
     return Arrays.asList(subDirectories); 
    } 
} 

Resultados:

searchFileWithFileUtils: 20186ms | searchFileRecursive: 1134ms | searchFileByDeepness: 16ms


[EDIT] También puede utilizar Java 8 Archivos de API para hacer este trabajo:

public static File searchFileJava8(final String rootFolder, final String fileName) { 
    File target = null; 
    Path root = Paths.get(rootFolder); 
    try (Stream<Path> stream = Files.find(root, Integer.MAX_VALUE, (path, attr) -> 
      path.getFileName().toString().equals(fileName))) { 
     Optional<Path> path = stream.findFirst(); 
     if(path.isPresent()) { 
      target = path.get().toFile(); 
     } 
    } 
    catch (IOException e) { 
    } 
    return target; 
} 

Pero el tiempo de ejecución no es mejor (994ms).

Cuestiones relacionadas