Wie liste ich rekursiv alle Dateien in einem Verzeichnis in Java auf? Bietet das Framework einen Nutzen?
Ich habe viele hackige Implementierungen gesehen. Aber keine aus dem Framework oder nio
Wie liste ich rekursiv alle Dateien in einem Verzeichnis in Java auf? Bietet das Framework einen Nutzen?
Ich habe viele hackige Implementierungen gesehen. Aber keine aus dem Framework oder nio
Antworten:
Java 8 bietet einen schönen Stream, um alle Dateien in einem Baum zu verarbeiten.
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
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.
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
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 .
Files.walk
mit einem parallelen Stream die beste zu sein, dicht gefolgt von Files.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 korrigieren
FileUtils haben iterateFiles
und listFiles
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).
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, wobei dir
sich ein Dateiobjekt befindet, das auf das Basisverzeichnis verweist.
listFilesAndDirs()
, da listFiles()
keine leeren Ordner zurückgegeben werden.
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.
// Bereit zu rennen
import java.io.File;
public class Filewalker {
public void walk( String path ) {
File root = new File( 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() );
}
}
}
public static void main(String[] args) {
Filewalker fw = new Filewalker();
fw.walk("c:\\" );
}
}
-> .
.
"/"
, "./"
oder "../"
für Stammverzeichnis, aktuelles Arbeitsverzeichnis und übergeordnetes Verzeichnis bzw.
Java 7 wird hat Files.walkFileTree :
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.
Zu dieser Frage gibt es jetzt ein komplettes Oracle-Tutorial .
Keine externen Bibliotheken erforderlich.
Gibt eine Sammlung zurück, damit Sie nach dem Anruf damit arbeiten können, was Sie wollen.
public static Collection<File> listFileTree(File dir) {
Set<File> fileTree = new HashSet<File>();
if(dir==null||dir.listFiles()==null){
return fileTree;
}
for (File entry : dir.listFiles()) {
if (entry.isFile()) fileTree.add(entry);
else fileTree.addAll(listFileTree(entry));
}
return fileTree;
}
Ich würde mit so etwas gehen wie:
public void 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.
listFiles()
: "Wenn dieser abstrakte Pfadname kein Verzeichnis bezeichnet, gibt diese Methode zurück null
."
Ich bevorzuge die Verwendung einer Warteschlange gegenüber der Rekursion für diese Art der einfachen Durchquerung:
List<File> allFiles = new ArrayList<File>();
Queue<File> dirs = new LinkedList<File>();
dirs.add(new File("/start/dir/"));
while (!dirs.isEmpty()) {
for (File f : dirs.poll().listFiles()) {
if (f.isDirectory()) {
dirs.add(f);
} else if (f.isFile()) {
allFiles.add(f);
}
}
}
Schreiben Sie es einfach selbst mit einer einfachen Rekursion:
public List<File> addFiles(List<File> files, File dir)
{
if (files == null)
files = new LinkedList<File>();
if (!dir.isDirectory())
{
files.add(dir);
return files;
}
for (File file : dir.listFiles())
addFiles(files, file);
return files;
}
Ich denke, das sollte die Arbeit machen:
File dir = new File(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 ( File
Klasse hat isDirectory()
Methode).
Mit Java 7 können Sie die folgende Klasse verwenden:
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class MyFileIterator extends SimpleFileVisitor<Path>
{
public MyFileIterator(String path) throws Exception
{
Files.walkFileTree(Paths.get(path), this);
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attributes) throws IOException
{
System.out.println("File: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attributes) throws IOException
{
System.out.println("Dir: " + dir);
return FileVisitResult.CONTINUE;
}
}
In Java 8 können wir jetzt das Dienstprogramm "Dateien" verwenden, um einen Dateibaum zu durchlaufen. Sehr einfach.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Dieser Code kann ausgeführt werden
public static void main(String... args) {
File[] files = new File("D:/").listFiles();
if (files != null)
getFiles(files);
}
public static void getFiles(File[] files) {
for (File file : files) {
if (file.isDirectory()) {
getFiles(file.listFiles());
} else {
System.out.println("File: " + file);
}
}
}
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.
public interface Visitor {
void visit(DirElement d);
void visit(FileElement f);
}
public abstract class Element {
protected File rootPath;
abstract void accept(Visitor v);
@Override
public String toString() {
return rootPath.getAbsolutePath();
}
}
public class FileElement extends Element {
FileElement(final String path) {
rootPath = new File(path);
}
@Override
void accept(final Visitor v) {
v.visit(this);
}
}
public class DirElement extends Element implements Iterable<Element> {
private final List<Element> elemList;
DirElement(final String path) {
elemList = new ArrayList<Element>();
rootPath = new File(path);
for (File f : rootPath.listFiles()) {
if (f.isDirectory()) {
elemList.add(new DirElement(f.getAbsolutePath()));
} else if (f.isFile()) {
elemList.add(new FileElement(f.getAbsolutePath()));
}
}
}
@Override
void accept(final Visitor v) {
v.visit(this);
}
public Iterator<Element> iterator() {
return elemList.iterator();
}
}
public class ElementWalker {
private final String rootDir;
ElementWalker(final String dir) {
rootDir = dir;
}
private void traverse() {
Element d = new DirElement(rootDir);
d.accept(new Walker());
}
public static void main(final String[] args) {
ElementWalker t = new ElementWalker("C:\\temp");
t.traverse();
}
private class Walker implements Visitor {
public void visit(final DirElement d) {
System.out.println(d);
for(Element e:d) {
e.accept(this);
}
}
public void visit(final FileElement f) {
System.out.println(f);
}
}
}
Sie können den folgenden Code verwenden, um eine Liste der Dateien eines bestimmten Ordners oder Verzeichnisses rekursiv abzurufen.
public static void main(String args[]) {
recusiveList("D:");
}
public static void recursiveList(String path) {
File f = new File(path);
File[] fl = f.listFiles();
for (int i = 0; i < fl.length; i++) {
if (fl[i].isDirectory() && !fl[i].isHidden()) {
System.out.println(fl[i].getAbsolutePath());
recusiveList(fl[i].getAbsolutePath());
} else {
System.out.println(fl[i].getName());
}
}
}
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.
try (Stream<Path> pathStream = Files.walk(Paths.get(path))
.filter(Files::isRegularFile)) {
for (Path file : (Iterable<Path>) pathStream::iterator) {
// something that throws IOException
Files.copy(file, System.out);
}
}
Den Trick hier gefunden: https://stackoverflow.com/a/32668807/1207791
Nicht rekursives BFS mit einer einzelnen Liste (besonderes Beispiel ist die Suche nach * .eml-Dateien):
final FileFilter filter = new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith(".eml");
}
};
// BFS recursive search
List<File> queue = new LinkedList<File>();
queue.addAll(Arrays.asList(dir.listFiles(filter)));
for (ListIterator<File> itr = queue.listIterator(); itr.hasNext();) {
File file = itr.next();
if (file.isDirectory()) {
itr.remove();
for (File f: file.listFiles(filter)) itr.add(f);
}
}
Meine Version (natürlich hätte ich den eingebauten Walk in Java 8 verwenden können ;-)):
public static List<File> findFilesIn(File rootDir, Predicate<File> predicate) {
ArrayList<File> collected = new ArrayList<>();
walk(rootDir, predicate, collected);
return collected;
}
private static void walk(File dir, Predicate<File> filterFunction, List<File> collected) {
Stream.of(listOnlyWhenDirectory(dir))
.forEach(file -> walk(file, filterFunction, addAndReturn(collected, file, filterFunction)));
}
private static File[] listOnlyWhenDirectory(File dir) {
return dir.isDirectory() ? dir.listFiles() : new File[]{};
}
private static List<File> addAndReturn(List<File> files, File toAdd, Predicate<File> filterFunction) {
if (filterFunction.test(toAdd)) {
files.add(toAdd);
}
return files;
}
Hier eine einfache, aber perfekt funktionierende Lösung mit recursion
:
public static List<Path> listFiles(String rootDirectory)
{
List<Path> files = new ArrayList<>();
listFiles(rootDirectory, files);
return files;
}
private static void listFiles(String path, List<Path> collectedFiles)
{
File root = new File(path);
File[] files = root.listFiles();
if (files == null)
{
return;
}
for (File file : files)
{
if (file.isDirectory())
{
listFiles(file.getAbsolutePath(), collectedFiles);
} else
{
collectedFiles.add(file.toPath());
}
}
}
private void fillFilesRecursively(File file, List<File> resultFiles) {
if (file.isFile()) {
resultFiles.add(file);
} else {
for (File child : file.listFiles()) {
fillFilesRecursively(child, resultFiles);
}
}
}
Ich habe mir das ausgedacht, um alle Dateien / Dateinamen rekursiv zu drucken.
private static void printAllFiles(String filePath,File folder) {
if(filePath==null) {
return;
}
File[] files = folder.listFiles();
for(File element : files) {
if(element.isDirectory()) {
printAllFiles(filePath,element);
} else {
System.out.println(" FileName "+ element.getName());
}
}
}
Beispielausgaben * .csv-Dateien im Verzeichnis rekursive Suche Unterverzeichnisse mit Files.find () aus java.nio:
String path = "C:/Daten/ibiss/ferret/";
logger.debug("Path:" + path);
try (Stream<Path> fileList = Files.find(Paths.get(path), Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile() && filePath.toString().endsWith("csv"))) {
List<String> someThingNew = fileList.sorted().map(String::valueOf).collect(Collectors.toList());
for (String t : someThingNew) {
t.toString();
logger.debug("Filename:" + t);
}
}
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.
Kotlin hat FileTreeWalk
zu diesem Zweck. Beispielsweise:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
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" %>
<%!
public List<String> files = new ArrayList<String>();
/**
Fills files array with all sub-files.
*/
public void 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 = new File(request.getRealPath(request.getServletPath()));
File dir = jsp.getParentFile();
walk(dir);
String prefixPath = dir.getAbsolutePath() + "/";
%>
Dann machst du einfach so etwas wie:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>