Ich habe gerade Testergebnisse abgeschlossen , die Leistungstests für viele der Antworten bereitstellen. Es überrascht nicht, dass alle NIO-basierten Antworten die beste Leistung erbringen. Die Commons-Io-Antwort ist eindeutig die schlechteste Leistung mit mehr als der doppelten Lauflänge.
Brett Ryan
2
Java8: Files.walk?
Benj
Antworten:
327
Java 8 bietet einen schönen Stream, um alle Dateien in einem Baum zu verarbeiten.
Dies bietet eine natürliche Möglichkeit, Dateien zu durchlaufen. Da es sich um einen Stream handelt, können Sie alle netten Stream-Operationen für das Ergebnis ausführen, z. B. Limit, Gruppierung, Zuordnung, vorzeitiges Beenden usw.
Beachten Sie, dass JavaDoc zwar nicht der Meinung ist, dass diese Methode effizienter als Files.walk sein könnte, aber effektiv identisch ist. Der Leistungsunterschied kann jedoch beobachtet werden, wenn Sie auch Dateiattribute in Ihrem Filter abrufen . Wenn Sie nach Attributen filtern müssen , verwenden Sie am Ende Files.find , andernfalls Files.walk , hauptsächlich, weil es Überladungen gibt und dies bequemer ist.
Eines dieser Beispiele, das die Magie der funktionalen Programmierung auch für Anfänger zeigen kann.
Johnny
2
Wie ist die Leistung im Vergleich zu Methoden vor Java 8? Mein aktueller Verzeichnisdurchlauf ist zu langsam und ich suche nach etwas, das ihn beschleunigt.
Sridhar Sarnobat
1
Ich schreibe einige Tests auf, die die meisten Varianten in den Antworten enthalten. Bisher scheint die Verwendung Files.walkmit einem parallelen Stream die beste zu sein, dicht gefolgt von Files.walkFileTreeder nur geringfügig langsameren. Die akzeptierte Antwort mit commons-io ist bei meinen Tests mit Abstand die langsamste und viermal langsamer.
Brett Ryan
1
@BrettRyan, ich habe deine Lösung ausprobiert, aber ich bekomme eine Ausnahme Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException. Wie könnte ich das korrigieren
Kachna
5
Wie erhalte ich daraus eine aktuelle Liste der Dateien?
Bearbeiten: Hier können Sie nach einem Benchmark für verschiedene Ansätze suchen. Es scheint, dass der Commons-Io-Ansatz langsam ist. Wählen Sie daher einige der schnelleren von hier aus (falls es darauf ankommt).
FYI / TLDR: Wenn Sie nur alle Dateien ohne Filterung rekursiv auflisten möchten, tun Sie dies FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE), wobei dirsich ein Dateiobjekt befindet, das auf das Basisverzeichnis verweist.
Andronikus
2
Möglicherweise möchten Sie die Verwendung in Betracht ziehen listFilesAndDirs(), da listFiles()keine leeren Ordner zurückgegeben werden.
Schnatterer
1
@ MikeFHay Wenn ich mir den FileUtils-Code anschaue, denke ich, dass das so sein könnte FileUtils.listFiles(dir, true, true). using FileUtils.listFiles(dir, null, true)löst eine Ausnahme aus, während FileUtils.listFiles(dir, true, null)alle Dateien aufgelistet werden, ohne in Unterverzeichnisse zu schauen.
Ocramot
Wie wäre es mit einer nativen JDK-Bibliothek? Ich kann dies einfach umsetzen, aber ich wäre einfach C & P von anderen Orten
Christian Bongiorno
1
Ich stelle einige Tests zusammen, aber bisher scheint dies viermal langsamer zu sein als bei Verwendung von JDK8- oder JDK7-Alternativen. Symlinks erweisen sich bei diesem Ansatz ebenfalls als problematisch, insbesondere wenn sie mit Verzeichnissen höher im Baum verknüpft sind. Dies führt dazu, dass die Methode niemals zurückkehrt. Dies kann durch die Behandlung des Filters vermieden werden, aber leider werden die Symlinks selbst nicht einmal als besucht eine Datei.
Brett Ryan
138
// Bereit zu rennen
import java.io.File;publicclassFilewalker{publicvoid walk(String path ){File root =newFile( path );File[] list = root.listFiles();if(list ==null)return;for(File f : list ){if( f.isDirectory()){
walk( f.getAbsolutePath());System.out.println("Dir:"+ f.getAbsoluteFile());}else{System.out.println("File:"+ f.getAbsoluteFile());}}}publicstaticvoid main(String[] args){Filewalker fw =newFilewalker();
fw.walk("c:\\");}}
Beachten Sie jedoch, dass bei symbolischen Links, die auf einen Pfad in der Pfadhierarchie verweisen, die Methode niemals endet. Stellen Sie sich einen Pfad mit einem Symlink vor, der auf zeigt -> ..
Brett Ryan
2
Dies ist im Wesentlichen eine schlechte Implementierung von Files.walkFileTree. Ich würde empfehlen, dass die Leute sich FIles.walkFileTree ansehen, anstatt zu versuchen, es selbst zu rollen ... Es hat eine Behandlung für das genaue Problem, auf das @BrettRyan hingewiesen hat.
Tyler Nichols
Vielen Dank für den Import java.io.File;. So viele Beispiele vergessen, das Namespace-Zeug oder sogar das Datentyp-Zeug einzuschließen, was das Beispiel zu einem Ausgangspunkt für eine Entdeckungsreise macht. Hier ist dieses Beispiel betriebsbereit. Vielen Dank.
Barrypicker
Der Pfad kann je nach Speicherort der Filewalker-Datei variieren. Verwendung "/", "./"oder "../"für Stammverzeichnis, aktuelles Arbeitsverzeichnis und übergeordnetes Verzeichnis bzw.
Wenn Sie einen Startpunkt und einen Dateibesucher angeben, werden beim Durchlaufen der Datei im Dateibaum verschiedene Methoden für den Dateibesucher aufgerufen. Wir erwarten, dass Benutzer dies verwenden, wenn sie eine rekursive Kopie, eine rekursive Verschiebung, eine rekursive Löschung oder eine rekursive Operation entwickeln, die Berechtigungen festlegt oder eine andere Operation für jede der Dateien ausführt.
publicvoid list(File file){System.out.println(file.getName());File[] children = file.listFiles();for(File child : children){
list(child);}}
Die Datei System.out.println dient nur dazu, anzuzeigen, dass etwas mit der Datei geschehen soll. Es ist nicht erforderlich, zwischen Dateien und Verzeichnissen zu unterscheiden, da eine normale Datei einfach keine untergeordneten Dateien hat.
Bitte! Lassen Sie den Aufrufer die Dateiliste initialisieren, damit er nicht jedes Mal die Nichtigkeit überprüfen muss. Wenn Sie eine zweite (öffentliche) Methode erstellen möchten, die die Liste erstellt, rufen Sie diese interne Methode auf und geben Sie die vollständige Liste zurück.
Helios
1
was auch immer. Ein Null-Check ist nicht sehr teuer, Bequemlichkeit + persönliche Vorlieben beiseite, ich denke, er wird den Punkt bekommen.
Pstanton
Können Sie das etwas ausführlicher erklären?
uday
8
Ich denke, das sollte die Arbeit machen:
File dir =newFile(dirname);String[] files = dir.list();
Auf diese Weise haben Sie Dateien und Verzeichnisse. Verwenden Sie jetzt die Rekursion und machen Sie dasselbe für dirs ( FileKlasse hat isDirectory()Methode).
Neben der rekursiven Durchquerung kann auch ein Besucherbasierter Ansatz verwendet werden.
Der folgende Code verwendet einen auf Besuchern basierenden Ansatz für die Durchquerung. Es wird erwartet, dass die Eingabe in das Programm das zu durchlaufende Stammverzeichnis ist.
Die akzeptierte Antwort ist großartig, bricht jedoch zusammen, wenn Sie E / A innerhalb des Lambda ausführen möchten.
Folgendes können Sie tun, wenn Ihre Aktion IOExceptions deklariert.
Sie können den gefilterten Stream als einen behandeln Iterableund dann Ihre Aktion in einer regulären for-each-Schleife ausführen. Auf diese Weise müssen Sie keine Ausnahmen innerhalb eines Lambda behandeln.
try(Stream<Path> pathStream =Files.walk(Paths.get(path)).filter(Files::isRegularFile)){for(Path file :(Iterable<Path>) pathStream::iterator){// something that throws IOExceptionFiles.copy(file,System.out);}}
Ich habe dieses Beispiel gepostet, da ich Probleme hatte zu verstehen, wie der Dateinamenparameter in dem von Bryan angegebenen Beispiel Nr. 1 mit foreach on Stream-result übergeben wird.
Erstellt eine Textliste aller Nicht-Verzeichnisdateien unter einem bestimmten Stammverzeichnis, eine Datei pro Zeile mit dem Pfad relativ zum Stammverzeichnis und zur Länge.
Basierend auf der Staplerantwort. Hier ist eine Lösung, die in JSP ohne externe Bibliotheken funktioniert, sodass Sie sie fast überall auf Ihrem Server ablegen können:
<!DOCTYPE html><%@ page session="false"%><%@ page import="java.util.*"%><%@ page import="java.io.*"%><%@ page contentType="text/html; charset=UTF-8"%><%!publicList<String> files =newArrayList<String>();/**
Fills files array with all sub-files.
*/publicvoid walk(File root ){File[] list = root.listFiles();if(list ==null)return;for(File f : list ){if( f.isDirectory()){
walk( f );}else{
files.add(f.getAbsolutePath());}}}%><%
files.clear();File jsp =newFile(request.getRealPath(request.getServletPath()));File dir = jsp.getParentFile();
walk(dir);String prefixPath = dir.getAbsolutePath()+"/";%>
Während es wahrscheinlich funktioniert, geht es um das Durchsuchen von Dateien und nicht um das Rendern von durchsuchten Dateien. Stellen Sie Ihren Algorithmus besser als solchen dar. Es wird nicht empfohlen, Geschäftslogik in eine JSP einzubetten.
Samuel Kerrien
Das hängt davon ab, was Sie tun. In einer Anwendung in Unternehmensgröße haben Sie absolut Recht. Wenn Sie dies nur als Drop-In für eine einfache, eigenständige Auflistung benötigen, ist dies vollkommen in Ordnung.
Antworten:
Java 8 bietet einen schönen Stream, um alle Dateien in einem Baum zu verarbeiten.
Dies bietet eine natürliche Möglichkeit, Dateien zu durchlaufen. Da es sich um einen Stream handelt, können Sie alle netten Stream-Operationen für das Ergebnis ausführen, z. B. Limit, Gruppierung, Zuordnung, vorzeitiges Beenden usw.
UPDATE : Ich möchte darauf hinweisen, dass es auch Files.find gibt, das ein BiPredicate verwendet , das effizienter sein könnte, wenn Sie Dateiattribute überprüfen müssen.
Beachten Sie, dass JavaDoc zwar nicht der Meinung ist, dass diese Methode effizienter als Files.walk sein könnte, aber effektiv identisch ist. Der Leistungsunterschied kann jedoch beobachtet werden, wenn Sie auch Dateiattribute in Ihrem Filter abrufen . Wenn Sie nach Attributen filtern müssen , verwenden Sie am Ende Files.find , andernfalls Files.walk , hauptsächlich, weil es Überladungen gibt und dies bequemer ist.
TESTS : Wie gewünscht habe ich einen Leistungsvergleich vieler Antworten bereitgestellt. Schauen Sie sich das Github-Projekt an, das Ergebnisse und einen Testfall enthält .
quelle
Files.walk
mit einem parallelen Stream die beste zu sein, dicht gefolgt vonFiles.walkFileTree
der nur geringfügig langsameren. Die akzeptierte Antwort mit commons-io ist bei meinen Tests mit Abstand die langsamste und viermal langsamer.Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Wie könnte ich das korrigierenFileUtils haben
iterateFiles
undlistFiles
Methoden. Probieren Sie es aus. (von commons-io )Bearbeiten: Hier können Sie nach einem Benchmark für verschiedene Ansätze suchen. Es scheint, dass der Commons-Io-Ansatz langsam ist. Wählen Sie daher einige der schnelleren von hier aus (falls es darauf ankommt).
quelle
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, wobeidir
sich ein Dateiobjekt befindet, das auf das Basisverzeichnis verweist.listFilesAndDirs()
, dalistFiles()
keine leeren Ordner zurückgegeben werden.FileUtils.listFiles(dir, true, true)
. usingFileUtils.listFiles(dir, null, true)
löst eine Ausnahme aus, währendFileUtils.listFiles(dir, true, null)
alle Dateien aufgelistet werden, ohne in Unterverzeichnisse zu schauen.// Bereit zu rennen
quelle
-> .
."/"
,"./"
oder"../"
für Stammverzeichnis, aktuelles Arbeitsverzeichnis und übergeordnetes Verzeichnis bzw.Java 7
wirdhat Files.walkFileTree :Zu dieser Frage gibt es jetzt ein komplettes Oracle-Tutorial .
quelle
Keine externen Bibliotheken erforderlich.
Gibt eine Sammlung zurück, damit Sie nach dem Anruf damit arbeiten können, was Sie wollen.
quelle
Ich würde mit so etwas gehen wie:
Die Datei System.out.println dient nur dazu, anzuzeigen, dass etwas mit der Datei geschehen soll. Es ist nicht erforderlich, zwischen Dateien und Verzeichnissen zu unterscheiden, da eine normale Datei einfach keine untergeordneten Dateien hat.
quelle
listFiles()
: "Wenn dieser abstrakte Pfadname kein Verzeichnis bezeichnet, gibt diese Methode zurücknull
."Ich bevorzuge die Verwendung einer Warteschlange gegenüber der Rekursion für diese Art der einfachen Durchquerung:
quelle
Schreiben Sie es einfach selbst mit einer einfachen Rekursion:
quelle
Ich denke, das sollte die Arbeit machen:
Auf diese Weise haben Sie Dateien und Verzeichnisse. Verwenden Sie jetzt die Rekursion und machen Sie dasselbe für dirs (
File
Klasse hatisDirectory()
Methode).quelle
Mit Java 7 können Sie die folgende Klasse verwenden:
quelle
In Java 8 können wir jetzt das Dienstprogramm "Dateien" verwenden, um einen Dateibaum zu durchlaufen. Sehr einfach.
quelle
Dieser Code kann ausgeführt werden
quelle
Neben der rekursiven Durchquerung kann auch ein Besucherbasierter Ansatz verwendet werden.
Der folgende Code verwendet einen auf Besuchern basierenden Ansatz für die Durchquerung. Es wird erwartet, dass die Eingabe in das Programm das zu durchlaufende Stammverzeichnis ist.
quelle
Sie können den folgenden Code verwenden, um eine Liste der Dateien eines bestimmten Ordners oder Verzeichnisses rekursiv abzurufen.
quelle
Die akzeptierte Antwort ist großartig, bricht jedoch zusammen, wenn Sie E / A innerhalb des Lambda ausführen möchten.
Folgendes können Sie tun, wenn Ihre Aktion IOExceptions deklariert.
Sie können den gefilterten Stream als einen behandeln
Iterable
und dann Ihre Aktion in einer regulären for-each-Schleife ausführen. Auf diese Weise müssen Sie keine Ausnahmen innerhalb eines Lambda behandeln.Den Trick hier gefunden: https://stackoverflow.com/a/32668807/1207791
quelle
Nicht rekursives BFS mit einer einzelnen Liste (besonderes Beispiel ist die Suche nach * .eml-Dateien):
quelle
Meine Version (natürlich hätte ich den eingebauten Walk in Java 8 verwenden können ;-)):
quelle
Hier eine einfache, aber perfekt funktionierende Lösung mit
recursion
:quelle
quelle
Ich habe mir das ausgedacht, um alle Dateien / Dateinamen rekursiv zu drucken.
quelle
Beispielausgaben * .csv-Dateien im Verzeichnis rekursive Suche Unterverzeichnisse mit Files.find () aus java.nio:
Ich habe dieses Beispiel gepostet, da ich Probleme hatte zu verstehen, wie der Dateinamenparameter in dem von Bryan angegebenen Beispiel Nr. 1 mit foreach on Stream-result übergeben wird.
Hoffe das hilft.
quelle
Kotlin hat
FileTreeWalk
zu diesem Zweck. Beispielsweise:Erstellt eine Textliste aller Nicht-Verzeichnisdateien unter einem bestimmten Stammverzeichnis, eine Datei pro Zeile mit dem Pfad relativ zum Stammverzeichnis und zur Länge.
quelle
Eine andere Möglichkeit ist auch dann, wenn jemand bereits Java 8 Walk bereitstellt.
Dieser liefert Ihnen alle Dateien rekursiv
quelle
Basierend auf der Staplerantwort. Hier ist eine Lösung, die in JSP ohne externe Bibliotheken funktioniert, sodass Sie sie fast überall auf Ihrem Server ablegen können:
Dann machst du einfach so etwas wie:
quelle