Wie iteriere ich durch die Dateien in einem Verzeichnis in Java?

175

Ich muss eine Liste aller Dateien in einem Verzeichnis erhalten, einschließlich der Dateien in allen Unterverzeichnissen. Was ist der Standardweg, um eine Verzeichnisiteration mit Java durchzuführen?

James
quelle

Antworten:

207

Sie können File#isDirectory()damit testen, ob die angegebene Datei (Pfad) ein Verzeichnis ist. Wenn dies der Fall ist true, rufen Sie dieselbe Methode mit ihrem File#listFiles()Ergebnis erneut auf. Dies wird als Rekursion bezeichnet .

Hier ist ein grundlegendes Kickoff-Beispiel.

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

Beachten Sie, dass dies empfindlich ist, StackOverflowErrorwenn der Baum tiefer ist, als der Stapel der JVM aufnehmen kann. Vielleicht möchten Sie stattdessen einen iterativen Ansatz oder eine Schwanzrekursion verwenden, aber das ist ein anderes Thema;)

BalusC
quelle
danke Balus, eine Idee, wie tief das als allgemeine Vermutung sein kann?
James
10
Hängt von den Speichereinstellungen Ihrer JVM ab. Aber im Allgemeinen so etwas wie ein paar Tausend. Wenn Sie glauben, jemals auf ein solches Verzeichnis zu stoßen, verwenden Sie keine Rekursion.
Mike Baranczak
4
Dies ist anfällig für einen Fall, NullPointerExceptionin dem sich das Dateisystem zwischen dem Aufruf von isDirectoryund dem Blockieren ändert oder Sie einfach Pech haben. Wenn Sie überprüfen, ob die Ausgabe von nicht null ist, wird diese Racebedingung gelöst. listFilesSystem.out.printlnlistFiles
Mike Samuel
1
@BoratSagdiyev, Nicht die alten Java-Datei-APIs verwenden, aber wenn Sie sich in einer modernen JVM befinden, java.nio.file.DirectoryStreamkönnen Sie über ein Verzeichnis iterieren und könnten so implementiert werden, dass sie einen geringen Speicherbedarf haben, aber der einzige Weg, dies sicher zu sagen, wäre um die Speichernutzung auf einer bestimmten Plattform zu überwachen.
Mike Samuel
1
"C: \\" Ordner ist nicht die beste Wahl eines Beispiels)
Vyacheslav
86

Wenn Sie Java 1.7 verwenden, können Sie verwenden java.nio.file.Files.walkFileTree(...).

Beispielsweise:

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

}

Wenn Sie Java 8 verwenden, können Sie die Stream-Schnittstelle verwenden mit 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
quelle
1
Gibt es eine Möglichkeit mit den Streams, einen Prüfpunkt zu setzen, wenn ein neues Verzeichnis durchlaufen wird, und eine Funktion auszuführen?
Raghu DV
28

Schauen Sie sich die FileUtils- Klasse in Apache Commons an - speziell iterateFiles :

Ermöglicht die Iteration der Dateien im angegebenen Verzeichnis (und optional der Unterverzeichnisse).

Ben J.
quelle
5
Diese API wird nicht wirklich gestreamt (wenn Sie sich für die Verwendung von Mem interessieren), sondern generiert zuerst eine Sammlung und gibt dann einen Iterator darüber zurück: return listFiles (directory, fileFilter, dirFilter) .iterator ();
Gili Nachum
Gute Option für Java 1.6.
David I.
Stimmen Sie mit @GiliNachum überein. FileUtils von Apache sammelt zuerst alle Dateien und gibt ihnen einen Iterator. Es ist schädlich für Ressourcen, wenn Sie eine große Menge an Dateien haben.
Bogdan Samondros
8

Für Java 7+ gibt es auch https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html

Beispiel aus dem 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
quelle
8

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

Verwenden Sie false, wenn Sie keine Dateien aus Unterverzeichnissen möchten.

fjkjava
quelle
3

Es ist ein Baum, also ist Rekursion Ihr Freund: Beginnen Sie mit dem übergeordneten Verzeichnis und rufen Sie die Methode auf, um ein Array von untergeordneten Dateien abzurufen. Durchlaufen Sie das untergeordnete Array. Wenn der aktuelle Wert ein Verzeichnis ist, übergeben Sie ihn an einen rekursiven Aufruf Ihrer Methode. Wenn nicht, verarbeiten Sie die Blattdatei entsprechend.

Duffymo
quelle
2

Wie bereits erwähnt, ist dies ein Rekursionsproblem. Insbesondere möchten Sie vielleicht einen Blick darauf werfen

listFiles() 

In der Java-Datei-API hier . Es gibt ein Array aller Dateien in einem Verzeichnis zurück. Verwenden Sie dies zusammen mit

isDirectory()

zu sehen, ob Sie weiter zurückgreifen müssen, ist ein guter Anfang.

Chimmy
quelle
Dieser Link kann von Nutzen sein, da der in der Antwort defekte ist.
Donglecow
0

Um mit @msandiford Antwort hinzuzufügen, möchten Sie in den meisten Fällen, wenn ein Dateibaum durchlaufen wird, eine Funktion als Verzeichnis ausführen oder eine bestimmte Datei wird besucht. Wenn Sie keine Streams verwenden möchten. Die folgenden überschriebenen Methoden können implementiert werden

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

Sie können File.list (FilenameFilter) (und Varianten) auch zum Durchlaufen von Dateien missbrauchen. Funktionscode und funktioniert in frühen Java-Versionen, zB:

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