Wie liste ich rekursiv alle Dateien unter einem Verzeichnis in Java auf? Bietet das Framework einen Nutzen?
Ich habe viele hackige Implementierungen gesehen. Aber keine aus dem Rahmen oder nio
Java 8 bietet einen Nice-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 Nice-Stream-Vorgänge für das Ergebnis ausführen, z. B. Begrenzen, Gruppieren, Zuordnen, vorzeitiges Beenden usw.
[~ # ~] Update [~ # ~] : Ich könnte darauf hinweisen, dass es auch Files.find gibt, das eine BiPredicate das könnte effizienter sein, 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 das JavaDoc nicht der Ansicht ist, dass diese Methode effizienter als Files.walk ist. Der Leistungsunterschied kann jedoch beobachtet werden, wenn Sie auch Dateiattribute in Ihrem Filter abrufen. Wenn Sie am Ende nach Attributen filtern müssen, verwenden Sie Files.find , andernfalls Files.walk , hauptsächlich, weil es Überladungen gibt und dies praktischer ist.
[~ # ~] Tests [~ # ~] : Wie angefordert habe ich einen Leistungsvergleich für viele der Antworten bereitgestellt. Schauen Sie sich das Github-Projekt an, das Ergebnisse und einen Testfall enthält .
FileUtils haben iterateFiles
und listFiles
Methoden. Probieren Sie es aus. (von commons-io )
Bearbeiten: Sie können hier überprüfen für einen Benchmark verschiedener Ansätze. Es scheint, dass der Commons-io-Ansatz langsam ist, also wählen Sie einige der schnelleren aus von hier aus (wenn es darauf ankommt)
// 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:\\" );
}
}
Java 7 werde haben hat Files.walkFileTree :
Wenn Sie einen Startpunkt und einen Dateibesucher angeben, werden beim Durchlaufen der Datei in der Dateibaumstruktur verschiedene Methoden für den Dateibesucher aufgerufen. Wir erwarten, dass die Benutzer dies verwenden, wenn sie eine rekursive Kopie, eine rekursive Verschiebung, ein rekursives Löschen oder eine rekursive Operation entwickeln, die Berechtigungen festlegt oder eine andere Operation für jede der Dateien ausführt.
Es gibt jetzt ein vollständiges Oracle-Tutorial zu dieser Frage .
Keine externen Bibliotheken erforderlich.
Gibt eine Sammlung zurück, sodass Sie nach dem Anruf mit ihr arbeiten können, was immer 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 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" gibt nur an, dass mit der Datei etwas geschehen soll. Es ist nicht erforderlich, zwischen Dateien und Verzeichnissen zu unterscheiden, da eine normale Datei lediglich null untergeordnete Dateien enthält.
Ich bevorzuge die Verwendung einer Warteschlange gegenüber der Rekursion für diese Art der einfachen Überquerung:
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);
}
}
}
schreibe es einfach selbst mit einfacher 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;
}
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;
}
}
Dieser Code ist betriebsbereit
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);
}
}
}
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).
In Java 8 können wir jetzt das Dienstprogramm "Files" verwenden, um einen Dateibaum zu durchsuchen. Sehr einfach.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Neben dem rekursiven Durchlaufen kann auch ein Besucher-basierter Ansatz verwendet werden.
Der folgende Code verwendet einen besucherbasierten Ansatz für die Durchquerung. Es wird erwartet, dass die Eingabe in das Programm das zu durchquerende 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());
}
}
}
Nicht rekursives BFS mit einer einzelnen Liste (spezielles Beispiel für 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);
}
}
Ich habe mir das ausgedacht, um alle Dateien/Dateinamen rekursiv auszudrucken.
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());
}
}
}
private void fillFilesRecursively(File file, List<File> resultFiles) {
if (file.isFile()) {
resultFiles.add(file);
} else {
for (File child : file.listFiles()) {
fillFilesRecursively(child, resultFiles);
}
}
}
Meine Version (natürlich hätte ich das eingebaute Walk-In verwenden können Java 8 ;-)):
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());
}
}
}
Beispiel für die Ausgabe von * .csv-Dateien in verzeichnisrekursiven Unterverzeichnissen 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);
}
}
Als ich dieses Beispiel postete, hatte ich Probleme zu verstehen, wie man den Dateinamen-Parameter in dem von Bryan gegebenen Beispiel Nr. 1 mit foreach auf Stream-result übergibt.
Hoffe das hilft.