Domanda

Come posso elencare ricorsivamente tutti i file sotto una directory in Java? Il framework fornisce qualsiasi utilità?

Ho visto molte implementazioni hacky. Ma nessuno dal framework o nio

È stato utile?

Soluzione

Java 8 fornisce un bel flusso per elaborare tutti i file in un albero.

Files.walk(Paths.get(path))
        .filter(Files::isRegularFile)
        .forEach(System.out::println);

Ciò fornisce un modo naturale per attraversare i file. Dal momento che è un flusso, puoi fare tutte le belle operazioni di flusso sul risultato come limite, raggruppamento, mappatura, uscire presto ecc.

AGGIORNARE: Potrei notare che c'è anche Files.find che richiede un Bipredicato Ciò potrebbe essere più efficiente se è necessario controllare gli attributi del file.

Files.find(Paths.get(path),
           Integer.MAX_VALUE,
           (filePath, fileAttr) -> fileAttr.isRegularFile())
        .forEach(System.out::println);

Si noti che mentre il Javadoc elude che questo metodo potrebbe essere più efficiente di Files.walk È effettivamente identico, la differenza nelle prestazioni può essere osservata se si sta recuperando anche gli attributi di file all'interno del filtro. Alla fine, se è necessario filtrare gli attributi, utilizzare Files.find, altrimenti usa Files.walk, soprattutto perché ci sono sovraccarichi ed è più conveniente.

Test: Come richiesto ho fornito un confronto delle prestazioni di molte delle risposte. Dai un'occhiata al Progetto GitHub che contiene risultati e un caso di test.

Altri suggerimenti

Fileutils avere iterateFiles e listFiles metodi. Provali. (da Commons-Io)

EDIT: puoi Controlla qui Per un punto di riferimento di diversi approcci. Sembra che l'approccio Commons-Io sia lento, quindi scegli alcuni dei più veloci da qui (se conta)

// Pronto a correre

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 avrà ha Files.walkfiletree:

Se si fornisce un punto di partenza e un visitatore di file, invocherà vari metodi sul visitatore del file mentre attraversa il file nell'albero del file. Ci aspettiamo che le persone lo utilizzino se stanno sviluppando una copia ricorsiva, una mossa ricorsiva, una eliminazione ricorsiva o un'operazione ricorsiva che imposta le autorizzazioni o esegue un'altra operazione su ciascuno dei file.

Ora c'è un intero Oracle Tutorial su questa domanda.

Non sono necessarie librerie esterne.
Restituisce una raccolta in modo da poter fare quello che vuoi con essa dopo la chiamata.

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

Andrei con qualcosa di simile:

public void list(File file) {
    System.out.println(file.getName());
    File[] children = file.listFiles();
    for (File child : children) {
        list(child);
    }
}

System.out.println è lì solo per indicare di fare qualcosa con il file. Non è necessario distinguere tra file e directory, poiché un file normale avrà semplicemente zero figli.

Preferisco usare una coda rispetto alla ricorsione per questo tipo di semplice attraversamento:

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

Scrivilo da solo usando una semplice ricorsione:

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

Con Java 7 puoi usare la seguente classe:

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

Penso che questo dovrebbe fare il lavoro:

File dir = new File(dirname);
String[] files = dir.list();

In questo modo hai file e dir. Ora usa la ricorsione e fai lo stesso per i dir (File la classe ha isDirectory() metodo).

Questo codice è pronto per essere eseguito

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

In Java 8, ora possiamo utilizzare l'utilità dei file per camminare su un albero di file. Molto semplice.

Files.walk(root.toPath())
      .filter(path -> !Files.isDirectory(path))
      .forEach(path -> System.out.println(path));

A parte il trasporto ricorsivo, si può utilizzare anche un approccio basato sui visitatori.

Di seguito è riportato il codice utilizza l'approccio basato sui visitatori per l'attraversamento. Si prevede che l'input per il programma sia la directory di root da attraversare.

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

È possibile utilizzare il codice di seguito per ottenere un elenco di file di cartelle o directory specifiche in modo ricorsivo.

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

BFS non ricorsivo con un singolo elenco (esempio particolare è la ricerca di file *.eml):

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

La mia versione (ovviamente avrei potuto usare la passeggiata incorporata in 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;
    }

Qui una soluzione semplice ma perfettamente funzionante utilizzando 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);
            }
        }
    }

Mi sono venuto in mente questo per la stampa di tutti i file/nomi di file in modo ricorsivo.

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

Esempio di output *.csv File in sottodirectory di ricerca ricorsiva directory utilizzando files.find () da 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);
        }

    }

Pubblicazione di questo esempio, dato che ho avuto difficoltà a capire Howto Pass the FileName nell'esempio n. 1 dato da Bryan, usando Foreach su Stream -Result -

Spero che sia di aiuto.

Basato sulla risposta di Stacker. Ecco una soluzione che funziona in JSP senza librerie esterne in modo da poterlo mettere quasi ovunque sul tuo server:

<!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() + "/";
%>

Allora fai solo qualcosa del tipo:

    <ul>
        <% for (String file : files) { %>
            <% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
                <li><%=file.replace(prefixPath, "")%></li>
            <% } %>
        <% } %>
    </ul>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top