Jak iterować pliki w katalogu w Javie?


175

Muszę uzyskać listę wszystkich plików w katalogu, w tym pliki we wszystkich podkatalogach. Jaki jest standardowy sposób wykonywania iteracji katalogu w języku Java?

Odpowiedzi:


207

Możesz użyć File#isDirectory()do sprawdzenia, czy podany plik (ścieżka) jest katalogiem. Jeśli tak jest true, po prostu wywołujesz tę samą metodę ponownie z jej File#listFiles()wynikiem. Nazywa się to rekurencją .

Oto podstawowy przykład rozpoczęcia.

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

Należy zauważyć, że jest to wrażliwe na sytuacje, StackOverflowErrorgdy drzewo jest głębsze niż może pomieścić stos maszyny JVM. Możesz zamiast tego użyć podejścia iteracyjnego lub rekurencji ogonowej , ale to inny temat;)


dzięki Balus, masz jakiś pomysł, jak głęboko to może być jako ogólne przypuszczenie?
James

10
Zależy od ustawień pamięci maszyny JVM. Ale ogólnie około kilka tysięcy. Jeśli myślisz, że kiedykolwiek natkniesz się na taki katalog, nie używaj rekursji.
Mike Baranczak

4
Jest to podatne na awarie, NullPointerExceptiongdy system plików zmienia się między wywołaniem isDirectoryi listFilesco może się zdarzyć, jeśli System.out.printlnblokuje się lub po prostu masz pecha. Sprawdzanie, czy wartość wyjściowa funkcji listFilesnie jest zerowa, rozwiązałoby ten warunek wyścigu.
Mike Samuel

1
@BoratSagdiyev, Nie korzystasz ze starych interfejsów API plików Java, ale jeśli korzystasz z nowoczesnej maszyny JVM, to java.nio.file.DirectoryStreampozwala na iterację w katalogu i może zostać zaimplementowana w celu uzyskania niewielkiej ilości pamięci, ale jedynym sposobem na upewnienie się byłoby do monitorowania wykorzystania pamięci na określonej platformie.
Mike Samuel

1
Folder „C: \\” nie jest najlepszym wyborem z przykładów)
Wiaczesław

86

Jeśli używasz Java 1.7, możesz użyć java.nio.file.Files.walkFileTree(...).

Na przykład:

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

}

Jeśli korzystasz z języka Java 8, możesz używać interfejsu strumieniowego z 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();
    }
  }

}

1
czy istnieje sposób ze strumieniami, aby umieścić punkt kontrolny, gdy przechodzi się do nowego katalogu i wykonuje funkcję?
Raghu DV

28

Sprawdź klasę FileUtils w Apache Commons - w szczególności iteruj pliki :

Umożliwia iterację po plikach w podanym katalogu (i opcjonalnie w jego podkatalogach).


5
To API nie jest w pełni strumieniowe (jeśli zależy Ci na wykorzystaniu pamięci), najpierw generuje kolekcję, a dopiero potem zwraca iterator: return listFiles (katalog, fileFilter, dirFilter) .iterator ();
Gili Nachum

Dobra opcja dla Java 1.6.
David I.

Zgadzam się z @GiliNachum. FileUtils firmy Apache najpierw zbiera wszystkie pliki i podaje dla nich iterator. Jeśli masz dużą liczbę plików, jest to szkodliwe dla zasobów.
Bogdan Samondros

8

W przypadku Java 7+ jest również https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html

Przykład zaczerpnięty z 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;
}

8

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

Użyj false, jeśli nie chcesz plików z podkatalogów.


3

To drzewo, więc rekurencja jest twoim przyjacielem: zacznij od katalogu nadrzędnego i wywołaj metodę, aby uzyskać tablicę plików potomnych. Wykonaj iterację przez tablicę potomną. Jeśli bieżąca wartość jest katalogiem, przekaż go do rekurencyjnego wywołania metody. Jeśli nie, odpowiednio przetwórz plik-liść.


2

Jak wspomniano, jest to problem rekursji. W szczególności warto się przyjrzeć

listFiles() 

W java File API tutaj . Zwraca tablicę wszystkich plików w katalogu. Używanie tego wraz z

isDirectory()

sprawdzenie, czy musisz powtórzyć dalej, to dobry początek.


Ten link może być przydatny, ponieważ ten w odpowiedzi jest uszkodzony.
Donglecow

0

Aby dodać odpowiedź @msandiford, ponieważ w większości przypadków, gdy chodzi się po drzewie plików, możesz chcieć wykonać funkcję jako katalog lub jakikolwiek określony plik jest odwiedzany. Jeśli niechętnie korzystasz ze strumieni. Można zaimplementować następujące metody

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

0

Możesz także nadużywać File.list (FilenameFilter) (i wariantów) do przechodzenia po plikach. Krótki kod i działa we wczesnych wersjach Java, np .:

// 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;
    }
});
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.