¿Cómo itero a través de los archivos en un directorio en Java?

175

Necesito obtener una lista de todos los archivos en un directorio, incluidos los archivos en todos los subdirectorios. ¿Cuál es la forma estándar de lograr la iteración de directorio con Java?

James
fuente

Respuestas:

207

Puede usar File#isDirectory()para probar si el archivo (ruta) dado es un directorio. Si es así true, simplemente vuelve a llamar al mismo método con su File#listFiles()resultado. Esto se llama recursividad .

Aquí hay un ejemplo básico de inicio.

public static void main(String... args) {
    File[] files = new File("C:/").listFiles();
    showFiles(files);
}

public static void showFiles(File[] files) {
    for (File file : files) {
        if (file.isDirectory()) {
            System.out.println("Directory: " + file.getName());
            showFiles(file.listFiles()); // Calls same method again.
        } else {
            System.out.println("File: " + file.getName());
        }
    }
}

Tenga en cuenta que esto es sensible a StackOverflowErrorcuando el árbol es más profundo de lo que puede contener la pila de la JVM. Es posible que desee utilizar un enfoque iterativo o recursión de cola en su lugar, pero ese es otro tema;)

BalusC
fuente
gracias Balus, ¿alguna idea de cuán profundo puede ser eso como una suposición general?
James
10
Depende de la configuración de memoria de su JVM. Pero generalmente algo así como unos pocos miles. Si cree que alguna vez podría encontrarse con un directorio como ese, entonces no use la recursividad.
Mike Baranczak 01 de
44
Esto es susceptible a NullPointerExceptioncuando el sistema de archivos cambia entre la llamada isDirectoryy, listFilescomo podría suceder si se System.out.printlnbloquean o simplemente tienes mala suerte. Verificar que la salida de listFilesno sea nula resolvería esa condición de carrera.
Mike Samuel
1
@BoratSagdiyev, no utiliza las API de archivos Java anteriores, pero si está en una JVM moderna, entonces le java.nio.file.DirectoryStreampermite iterar sobre un directorio, y podría implementarse para tener una pequeña huella de memoria, pero la única forma de saberlo con certeza sería para monitorear el uso de memoria en una plataforma en particular.
Mike Samuel
1
La carpeta "C: \\" no es la mejor opción de un ejemplo)
Vyacheslav
86

Si está utilizando Java 1.7, puede usarlo java.nio.file.Files.walkFileTree(...).

Por ejemplo:

public class WalkFileTreeExample {

  public static void main(String[] args) {
    Path p = Paths.get("/usr");
    FileVisitor<Path> fv = new SimpleFileVisitor<Path>() {
      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
          throws IOException {
        System.out.println(file);
        return FileVisitResult.CONTINUE;
      }
    };

    try {
      Files.walkFileTree(p, fv);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

Si está utilizando Java 8, puede usar la interfaz de transmisión con java.nio.file.Files.walk(...):

public class WalkFileTreeExample {

  public static void main(String[] args) {
    try (Stream<Path> paths = Files.walk(Paths.get("/usr"))) {
      paths.forEach(System.out::println);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}
clstrfsck
fuente
1
¿Hay alguna manera con las corrientes para poner un punto de control cuando se recorre un nuevo directorio y ejecuta una función?
Raghu DV
28

Consulte la clase FileUtils en Apache Commons, específicamente iterateFiles :

Permite la iteración sobre los archivos en un directorio dado (y opcionalmente sus subdirectorios).

Ben J
fuente
55
Esta API no es realmente streaming (si te importa el uso de mem), primero genera una colección, solo luego devuelve un iterador sobre ella: return listFiles (directorio, fileFilter, dirFilter) .iterator ();
Gili Nachum
Buena opción para Java 1.6.
David I.
De acuerdo con @GiliNachum. FileUtils de Apache primero recopila todos los archivos y les proporciona un iterador. Es perjudicial para los recursos si tiene una gran cantidad de archivos.
Bogdan Samondros
8

Para Java 7+, también hay https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html

Ejemplo tomado del Javadoc:

List<Path> listSourceFiles(Path dir) throws IOException {
   List<Path> result = new ArrayList<>();
   try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.{c,h,cpp,hpp,java}")) {
       for (Path entry: stream) {
           result.add(entry);
       }
   } catch (DirectoryIteratorException ex) {
       // I/O error encounted during the iteration, the cause is an IOException
       throw ex.getCause();
   }
   return result;
}
Wim Deblauwe
fuente
8

Utilizando org.apache.commons.io.FileUtils

File file = new File("F:/Lines");       
Collection<File> files = FileUtils.listFiles(file, null, true);     
for(File file2 : files){
    System.out.println(file2.getName());            
} 

Use false si no desea archivos de subdirectorios.

fjkjava
fuente
3

Es un árbol, así que la recursión es tu amiga: comienza con el directorio principal y llama al método para obtener una matriz de archivos secundarios. Iterar a través de la matriz secundaria. Si el valor actual es un directorio, páselo a una llamada recursiva de su método. De lo contrario, procese el archivo de hoja adecuadamente.

duffymo
fuente
2

Como se señaló, este es un problema de recurrencia. En particular, es posible que desee mirar

listFiles() 

En la API de Java File aquí . Devuelve una matriz de todos los archivos en un directorio. Usando esto junto con

isDirectory()

para ver si necesita repetir aún más es un buen comienzo.

Chimmy
fuente
Este enlace puede ser útil ya que el que está en la respuesta está roto.
Donglecow
0

Para agregar con la respuesta @msandiford, como la mayoría de las veces cuando se recorre un árbol de archivos, es posible que desee ejecutar una función como un directorio o se visita cualquier archivo en particular. Si eres reacio a usar corrientes. Se pueden implementar los siguientes métodos anulados

Files.walkFileTree(Paths.get(Krawl.INDEXPATH), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
    new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException {
                // Do someting before directory visit
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException {
                // Do something when a file is visited
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                throws IOException {
                // Do Something after directory visit 
                return FileVisitResult.CONTINUE;
        }
});
Raghu DV
fuente
0

También puede usar incorrectamente File.list (FilenameFilter) (y variantes) para el recorrido del archivo. Código corto y funciona en versiones tempranas de Java, por ejemplo:

// list files in dir
new File(dir).list(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        String file = dir.getAbsolutePath() + File.separator + name;
        System.out.println(file);
        return false;
    }
});
Rob Klinkhamer
fuente