¿Cómo enumero recursivamente todos los archivos en un directorio en Java? ¿El marco proporciona alguna utilidad?
Vi muchas implementaciones hacky. Pero ninguno del framework o nio
¿Cómo enumero recursivamente todos los archivos en un directorio en Java? ¿El marco proporciona alguna utilidad?
Vi muchas implementaciones hacky. Pero ninguno del framework o nio
Respuestas:
Java 8 proporciona una buena secuencia para procesar todos los archivos en un árbol.
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
Esto proporciona una forma natural de atravesar archivos. Dado que es una secuencia, puede realizar todas las operaciones de secuencia agradables en el resultado, como límite, agrupación, asignación, salida anticipada, etc.
ACTUALIZACIÓN : podría señalar que también hay Files.find, que toma un BiPredicate que podría ser más eficiente si necesita verificar los atributos del archivo.
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
Tenga en cuenta que si bien JavaDoc elude que este método podría ser más eficiente que Files.walk , es efectivamente idéntico, la diferencia en el rendimiento se puede observar si también está recuperando atributos de archivo dentro de su filtro. Al final, si necesita filtrar los atributos, use Files.find , de lo contrario use Files.walk , principalmente porque hay sobrecargas y es más conveniente.
PRUEBAS : Según lo solicitado, proporcioné una comparación de rendimiento de muchas de las respuestas. Echa un vistazo al proyecto Github que contiene resultados y un caso de prueba .
Files.walk
con un flujo paralelo es el mejor, seguido de cerca por lo Files.walkFileTree
cual es solo un poco más lento. La respuesta aceptada usando commons-io es, con mucho, la más lenta, según mis pruebas, 4 veces más lenta.
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. ¿Cómo podría corregirlo
FileUtils tiene iterateFiles
y listFiles
métodos. Pruébalos (de commons-io )
Editar: puede consultar aquí para obtener un punto de referencia de diferentes enfoques. Parece que el enfoque commons-io es lento, así que elija algunos de los más rápidos desde aquí (si es importante)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, donde dir
hay un objeto File que apunta al directorio base.
listFilesAndDirs()
, ya listFiles()
que no devuelve carpetas vacías.
FileUtils.listFiles(dir, true, true)
. utilizando FileUtils.listFiles(dir, null, true)
arrojará una excepción, mientras FileUtils.listFiles(dir, true, null)
que enumerará todos los archivos sin buscar en subdirectorios.
// Listo para correr
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 "../"
para el directorio raíz, el directorio de trabajo actual y el directorio principal, respectivamente
Java 7 tendrá tiene Files.walkFileTree :
Si proporciona un punto de partida y un visitante de archivos, invocará varios métodos en el visitante de archivos mientras recorre el archivo en el árbol de archivos. Esperamos que las personas usen esto si están desarrollando una copia recursiva, un movimiento recursivo, una eliminación recursiva o una operación recursiva que establece permisos o realiza otra operación en cada uno de los archivos.
Ahora hay un tutorial Oracle sobre esta pregunta .
No se necesitan bibliotecas externas.
Devuelve una Colección para que pueda hacer lo que quiera después de la llamada.
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;
}
Yo iría con algo como:
public void list(File file) {
System.out.println(file.getName());
File[] children = file.listFiles();
for (File child : children) {
list(child);
}
}
System.out.println está ahí para indicar que debe hacer algo con el archivo. no es necesario diferenciar entre archivos y directorios, ya que un archivo normal simplemente tendrá cero hijos.
listFiles()
: "Si este nombre de ruta abstracto no denota un directorio, entonces este método regresa null
".
Prefiero usar una cola sobre la recursividad para este tipo de conversión 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);
}
}
}
solo escríbelo tú mismo usando una recursión 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;
}
Creo que esto debería hacer el trabajo:
File dir = new File(dirname);
String[] files = dir.list();
De esta manera tienes archivos y directorios. Ahora use la recursión y haga lo mismo para los directorios (la File
clase tiene isDirectory()
método).
Con Java 7 puede usar la siguiente clase:
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;
}
}
En Java 8, ahora podemos usar la utilidad Archivos para recorrer un árbol de archivos. Muy simple.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Este código está listo para ejecutarse
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);
}
}
}
Además del recorrido recursivo, también se puede utilizar un enfoque basado en el visitante.
El siguiente código utiliza el enfoque basado en el visitante para el recorrido. Se espera que la entrada al programa sea el directorio raíz para atravesar.
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);
}
}
}
Puede usar el siguiente código para obtener una lista de archivos de carpetas o directorios específicos de forma recursiva.
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 respuesta aceptada es excelente, sin embargo, se descompone cuando quieres hacer IO dentro de la lambda.
Esto es lo que puede hacer si su acción declara IOExceptions.
Puede tratar la secuencia filtrada como un Iterable
, y luego realizar su acción en un ciclo regular para cada ciclo. De esta manera, no tiene que manejar excepciones dentro de 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);
}
}
Encontré ese truco aquí: https://stackoverflow.com/a/32668807/1207791
BFS no recursivo con una sola lista (un ejemplo particular es buscar archivos * .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);
}
}
Mi versión (por supuesto, podría haber utilizado el paseo incorporado en 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;
}
Aquí una solución simple pero que funciona perfectamente usando 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);
}
}
}
Se me ocurrió esto para imprimir todos los archivos / nombres de archivo de forma recursiva.
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());
}
}
}
El ejemplo genera archivos * .csv en subdirectorios de búsqueda recursiva de directorios usando Files.find () 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);
}
}
Publicando este ejemplo, ya que tuve problemas para entender cómo pasar el parámetro del nombre de archivo en el ejemplo # 1 dado por Bryan, usando foreach en Stream-result -
Espero que esto ayude.
Kotlin tiene FileTreeWalk
para este propósito. Por ejemplo:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Producirá una lista de texto de todos los archivos que no son de directorio bajo una raíz determinada, un archivo por línea con la ruta relativa a la raíz y la longitud.
Basado en la respuesta del apilador. Aquí hay una solución que funciona en JSP sin bibliotecas externas para que pueda colocarla en casi cualquier lugar de su servidor:
<!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() + "/";
%>
Entonces solo haces algo como:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>