Come posso elencare ricorsivamente tutti i file in una directory in Java? Il framework fornisce qualche utilità?
Ho visto molte implementazioni confuse. Ma nessuno dal framework o nio
Come posso elencare ricorsivamente tutti i file in una directory in Java? Il framework fornisce qualche utilità?
Ho visto molte implementazioni confuse. Ma nessuno dal framework o nio
Risposte:
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 del flusso sul risultato come limite, raggruppamento, mappatura, uscita anticipata ecc.
AGGIORNAMENTO : Potrei sottolineare che c'è anche Files.find che accetta un BiPredicate che 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 JavaDoc elude che questo metodo potrebbe essere più efficiente di Files.walk è effettivamente identico, la differenza nelle prestazioni può essere osservata se si stanno anche recuperando gli attributi di file all'interno del filtro. Alla fine, se hai bisogno di filtrare gli attributi usa Files.find , altrimenti usa Files.walk , principalmente perché ci sono sovraccarichi ed è più conveniente.
PROVE : 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 .
Files.walk
con un flusso parallelo sia il migliore, seguito da vicino Files.walkFileTree
che è solo leggermente più lento. La risposta accettata utilizzando commons-io è di gran lunga la più lenta dai miei test per essere 4 volte più lenta.
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Come potrei correggerlo
FileUtils ha iterateFiles
e listFiles
metodi. Provali. (da commons-io )
Modifica: è possibile verificare qui un punto di riferimento di diversi approcci. Sembra che l'approccio commons-io sia lento, quindi scegli alcuni tra quelli più veloci da qui (se è importante)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, dove si dir
trova un oggetto File che punta alla directory di base.
listFilesAndDirs()
, in quanto listFiles()
non restituisce cartelle vuote.
FileUtils.listFiles(dir, true, true)
. l'utilizzo FileUtils.listFiles(dir, null, true)
genererà un'eccezione, mentre FileUtils.listFiles(dir, true, null)
elencherà tutti i file senza cercare nelle sottodirectory.
// 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:\\" );
}
}
-> .
.
"/"
, "./"
o "../"
per la directory principale, la directory di lavoro corrente e la directory principale, rispettivamente
Java 7 avrà ha Files.walkFileTree :
Se si fornisce un punto di partenza e un visitatore di file, invocherà vari metodi sul visitatore di file mentre percorre il file nella struttura dei file. Ci aspettiamo che le persone lo utilizzino se stanno sviluppando una copia ricorsiva, una mossa ricorsiva, un'eliminazione ricorsiva o un'operazione ricorsiva che imposta le autorizzazioni o esegue un'altra operazione su ciascuno dei file.
Ora c'è un intero tutorial Oracle su questa domanda .
Non sono necessarie librerie esterne.
Restituisce una raccolta in modo che tu possa fare quello che vuoi 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;
}
Vorrei andare 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 è proprio lì per indicare di fare qualcosa con il file. non è necessario distinguere tra file e directory, poiché un file normale avrà semplicemente zero figli.
listFiles()
: "Se questo percorso astratto non indica una directory, questo metodo restituisce null
."
Preferisco usare una coda rispetto alla ricorsione per questo tipo di semplice traversione:
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 tu stesso 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;
}
Penso che questo dovrebbe fare il lavoro:
File dir = new File(dirname);
String[] files = dir.list();
In questo modo hai file e directory. Ora usa la ricorsione e fai lo stesso per dirs (la File
classe ha il isDirectory()
metodo).
Con Java 7 è possibile utilizzare 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;
}
}
In Java 8, ora possiamo usare l'utilità File per percorrere un albero di file. Molto semplice.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
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);
}
}
}
Oltre all'attraversamento ricorsivo, si può usare anche un approccio basato sul Visitatore.
Sotto il codice viene utilizzato l'approccio basato sul visitatore per l'attraversamento. Si prevede che l'input per il programma sia la directory principale 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 seguente per ottenere ricorsivamente un elenco di file di cartelle o directory specifiche.
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());
}
}
}
La risposta accettata è ottima, tuttavia si interrompe quando si desidera eseguire IO all'interno della lambda.
Ecco cosa puoi fare se la tua azione dichiara IOExceptions.
Puoi considerare il flusso filtrato come un Iterable
, quindi eseguire la tua azione in un normale ciclo per ogni. In questo modo, non è necessario gestire le eccezioni all'interno di una lambda.
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);
}
}
Ho trovato questo trucco qui: https://stackoverflow.com/a/32668807/1207791
BFS non ricorsivo con un unico elenco (un 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 integrata 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;
}
Ecco una soluzione semplice ma perfettamente funzionante che utilizza 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);
}
}
}
Ho pensato a questo per stampare ricorsivamente tutti i file / nomi di file.
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 nella directory ricorsiva ricerca nelle sottodirectory usando 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);
}
}
Pubblicando questo esempio, poiché ho avuto difficoltà a capire come passare il parametro del nome file nell'esempio n. 1 fornito da Bryan, usando foreach su Stream-result -
Spero che questo ti aiuti.
Kotlin ha FileTreeWalk
a questo scopo. Per esempio:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Produrrà un elenco di testo di tutti i file non di directory in una determinata radice, un file per riga con il percorso relativo alla radice e alla lunghezza.
Basato sulla risposta dell'impilatore. Ecco una soluzione che funziona in JSP senza librerie esterne in modo da poterla collocare praticamente 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() + "/";
%>
Quindi fai semplicemente qualcosa del tipo:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>