Como faço para percorrer os arquivos em um diretório em Java?

175

Eu preciso obter uma lista de todos os arquivos em um diretório, incluindo arquivos em todos os subdiretórios. Qual é a maneira padrão de realizar a iteração de diretório com Java?

James
fonte

Respostas:

207

Você pode usar File#isDirectory()para testar se o arquivo (caminho) fornecido é um diretório. Se for esse o caso true, basta chamar o mesmo método novamente com o File#listFiles()resultado. Isso é chamado de recursão .

Aqui está um exemplo básico de kickoff.

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

Observe que isso é sensível a StackOverflowErrorquando a árvore é mais profunda do que a pilha da JVM pode suportar. Você pode usar uma abordagem iterativa ou recursão de cauda , mas esse é outro assunto;)

BalusC
fonte
obrigado Balus, alguma idéia de quão profundo isso pode ser como um palpite geral?
James
10
Depende das configurações de memória da sua JVM. Mas geralmente algo como alguns milhares. Se você acha que pode encontrar um diretório assim, não use recursão.
Mike Baranczak
4
Isso é suscetível a NullPointerExceptionquando o sistema de arquivos muda entre a chamada isDirectorye, listFilescomo pode acontecer, se o System.out.printlnbloqueio for bloqueado ou você simplesmente tiver azar. Verificar se a saída de listFilesnão é nula resolveria essa condição de corrida.
Mike Samuel
1
@BoratSagdiyev, Não usando as APIs de arquivos Java antigas, mas se você estiver em uma JVM moderna, ele java.nio.file.DirectoryStreampermitirá a iteração em um diretório e poderá ser implementado para ter uma pequena quantidade de memória, mas a única maneira de dizer com certeza seria para monitorar o uso de memória em uma plataforma específica.
Mike Samuel
1
"C: \\" pasta não é a melhor escolha de um exemplo)
Vyacheslav
86

Se você estiver usando o Java 1.7, poderá usar java.nio.file.Files.walkFileTree(...).

Por exemplo:

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

}

Se você estiver usando o Java 8, poderá usar a interface de fluxo com 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
fonte
1
existe uma maneira com os fluxos de colocar um ponto de verificação quando um novo diretório é percorrido e executar uma função?
Raghu DV
28

Confira a classe FileUtils no Apache Commons - especificamente iterateFiles :

Permite a iteração sobre os arquivos no diretório especificado (e opcionalmente em seus subdiretórios).

Ben J
fonte
5
Essa API não é verdadeiramente streaming (se você se preocupa com o uso de mem), primeiro gera uma coleção e, em seguida, retorna um iterador sobre ela: return listFiles (directory, fileFilter, dirFilter) .iterator ();
Gili Nachum
Boa opção para Java 1.6.
David I.
Concorde com @GiliNachum. O FileUtils by Apache primeiro coleta todos os arquivos e fornece um iterador para eles. É prejudicial para os recursos se você tiver uma quantidade enorme de arquivos.
Bogdan Samondros
8

Para Java 7+, também há https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html

Exemplo retirado do 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
fonte
8

Usando 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 se não desejar arquivos de subdiretórios.

fjkjava
fonte
3

É uma árvore, então a recursão é sua amiga: comece com o diretório pai e chame o método para obter uma matriz de arquivos filhos. Iterar através da matriz filho. Se o valor atual for um diretório, passe-o para uma chamada recursiva do seu método. Caso contrário, processe o arquivo folha adequadamente.

duffymo
fonte
2

Como observado, esse é um problema de recursão. Em particular, você pode querer olhar para

listFiles() 

Na API do arquivo java aqui . Retorna uma matriz de todos os arquivos em um diretório. Usando isso junto com

isDirectory()

ver se é necessário recuar mais é um bom começo.

Chimmy
fonte
Esse link pode ser útil, pois o da resposta está quebrado.
Donglecow 7/06
0

Para adicionar com a resposta @msandiford, como na maioria das vezes em que uma árvore de arquivos é percorrida, você pode querer executar uma função como um diretório ou visitar qualquer arquivo específico. Se você estiver relutante em usar fluxos. Os seguintes métodos substituídos podem ser implementados

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
fonte
0

Você também pode usar incorretamente o File.list (FilenameFilter) (e variantes) para a passagem do arquivo. Código curto e funciona em versões java anteriores, por exemplo:

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