Comment lister récursivement tous les fichiers d'un répertoire en Java? Le cadre fournit-il une utilité?
J'ai vu beaucoup d'implémentations hacky. Mais aucun du cadre ou nio
Comment lister récursivement tous les fichiers d'un répertoire en Java? Le cadre fournit-il une utilité?
J'ai vu beaucoup d'implémentations hacky. Mais aucun du cadre ou nio
Réponses:
Java 8 fournit un joli flux pour traiter tous les fichiers d'une arborescence.
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
Cela fournit un moyen naturel de parcourir les fichiers. Puisqu'il s'agit d'un flux, vous pouvez effectuer toutes les belles opérations de flux sur le résultat, telles que la limite, le regroupement, le mappage, la sortie anticipée, etc.
MISE À JOUR : Je pourrais souligner qu'il existe également Files.find qui prend un BiPredicate qui pourrait être plus efficace si vous devez vérifier les attributs de fichier.
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
Notez que bien que JavaDoc élude que cette méthode pourrait être plus efficace que Files.walk, elle est effectivement identique, la différence de performances peut être observée si vous récupérez également des attributs de fichier dans votre filtre. En fin de compte, si vous devez filtrer les attributs, utilisez Files.find , sinon utilisez Files.walk , principalement parce qu'il y a des surcharges et c'est plus pratique.
TESTS : Comme demandé, j'ai fourni une comparaison des performances de la plupart des réponses. Découvrez le projet Github qui contient des résultats et un cas de test .
Files.walk
avec un flux parallèle soit la meilleure, suivie de près par Files.walkFileTree
laquelle n'est que légèrement plus lente. La réponse acceptée en utilisant commons-io est de loin la plus lente de mes tests à être 4 fois plus lente.
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Comment pourrais-je le corriger
FileUtils ont iterateFiles
et listFiles
méthodes. Essayez-les. (de commons-io )
Edit: Vous pouvez vérifier ici pour un benchmark des différentes approches. Il semble que l'approche commune-io soit lente, alors choisissez quelques-unes des plus rapides d'ici (si cela est important)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, où dir
est un objet File qui pointe vers le répertoire de base.
listFilesAndDirs()
, car listFiles()
ne renvoie pas de dossiers vides.
FileUtils.listFiles(dir, true, true)
. using FileUtils.listFiles(dir, null, true)
lèvera une exception, tandis que FileUtils.listFiles(dir, true, null)
listera tous les fichiers sans regarder dans les sous-répertoires.
// Prêt à courir
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:\\" );
}
}
-> .
.
"/"
, "./"
ou "../"
pour le répertoire racine, le répertoire de travail actuel et le répertoire parent, respectivement
Java 7 aura a Files.walkFileTree :
Si vous fournissez un point de départ et un visiteur de fichier, il invoquera diverses méthodes sur le visiteur de fichier pendant qu'il parcourt le fichier dans l'arborescence de fichiers. Nous nous attendons à ce que les utilisateurs l'utilisent s'ils développent une copie récursive, un déplacement récursif, une suppression récursive ou une opération récursive qui définit des autorisations ou effectue une autre opération sur chacun des fichiers.
Il existe désormais un didacticiel Oracle complet sur cette question .
Aucune bibliothèque externe requise.
Renvoie une collection pour que vous puissiez en faire ce que vous voulez après l'appel.
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;
}
J'irais avec quelque chose comme:
public void list(File file) {
System.out.println(file.getName());
File[] children = file.listFiles();
for (File child : children) {
list(child);
}
}
Le System.out.println est juste là pour indiquer de faire quelque chose avec le fichier. il n'est pas nécessaire de faire la différence entre les fichiers et les répertoires, car un fichier normal n'aura simplement aucun enfant.
listFiles()
: "Si ce nom de chemin abstrait ne désigne pas un répertoire, alors cette méthode revient null
."
Je préfère utiliser une file d'attente à la récursivité pour ce type de traversion simple:
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);
}
}
}
écrivez-le vous-même en utilisant une récursivité simple:
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;
}
Je pense que cela devrait faire le travail:
File dir = new File(dirname);
String[] files = dir.list();
De cette façon, vous avez des fichiers et des répertoires. Utilisez maintenant la récursivité et faites de même pour les répertoires (la File
classe a une isDirectory()
méthode).
Avec Java 7, vous pouvez utiliser la classe suivante:
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;
}
}
Dans Java 8, nous pouvons désormais utiliser l'utilitaire Fichiers pour parcourir une arborescence de fichiers. Très simple.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Ce code est prêt à fonctionner
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);
}
}
}
Outre la traversée récursive, on peut également utiliser une approche basée sur les visiteurs.
Le code ci-dessous utilise l'approche basée sur les visiteurs pour la traversée. On s'attend à ce que l'entrée du programme soit le répertoire racine à parcourir.
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);
}
}
}
Vous pouvez utiliser le code ci-dessous pour obtenir une liste de fichiers d'un dossier ou répertoire spécifique de manière récursive.
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 réponse acceptée est excellente, mais elle tombe en panne lorsque vous voulez faire des E / S à l'intérieur du lambda.
Voici ce que vous pouvez faire si votre action déclare des IOExceptions.
Vous pouvez traiter le flux filtré comme un Iterable
, puis effectuer votre action dans une boucle régulière pour chaque. De cette façon, vous n'avez pas à gérer les exceptions à l'intérieur d'un 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);
}
}
Trouvé cette astuce ici: https://stackoverflow.com/a/32668807/1207791
BFS non récursif avec une seule liste (un exemple particulier est la recherche de fichiers * .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);
}
}
Ma version (bien sûr j'aurais pu utiliser la promenade intégrée dans 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;
}
Voici une solution simple mais parfaitement fonctionnelle utilisant 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);
}
}
}
Je suis venu avec ceci pour imprimer tous les fichiers / noms de fichiers récursivement.
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());
}
}
}
L'exemple génère des fichiers * .csv dans le répertoire de recherche récursive des sous-répertoires à l'aide de Files.find () à partir de 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);
}
}
Publier cet exemple, car j'ai eu du mal à comprendre comment passer le paramètre de nom de fichier dans l'exemple n ° 1 donné par Bryan, en utilisant foreach sur Stream-result -
J'espère que cela t'aides.
Kotlin a FileTreeWalk
à cet effet. Par exemple:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Produira une liste de textes de tous les fichiers non-répertoires sous une racine donnée, un fichier par ligne avec le chemin relatif à la racine et la longueur.
Basé sur la réponse de l'empileur. Voici une solution fonctionnant dans JSP sans aucune bibliothèque externe afin que vous puissiez la mettre presque n'importe où sur votre serveur:
<!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() + "/";
%>
Ensuite, vous faites simplement quelque chose comme:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>