Tengo esta función que imprime el nombre de todos los archivos en un directorio de forma recursiva. El problema es que mi código es muy lento porque tiene que acceder a un dispositivo de red remoto con cada iteración.
Mi plan es cargar primero todos los archivos del directorio de forma recursiva y luego revisar todos los archivos con la expresión regular para filtrar todos los archivos que no quiero. ¿Alguien tiene una sugerencia mejor?
public static printFnames(String sDir){
File[] faFiles = new File(sDir).listFiles();
for(File file: faFiles){
if(file.getName().matches("^(.*?)")){
System.out.println(file.getAbsolutePath());
}
if(file.isDirectory()){
printFnames(file.getAbsolutePath());
}
}
}
Esto es solo una prueba más adelante. No voy a usar el código como este, sino que voy a agregar la ruta y la fecha de modificación de cada archivo que coincida con una expresión regular avanzada en una matriz.
Respuestas:
Suponiendo que este es el código de producción real que va a escribir, sugiero usar la solución para este tipo de cosas que ya se han resuelto: Apache Commons IO , específicamente
FileUtils.listFiles()
. Maneja directorios anidados, filtros (basados en nombre, fecha de modificación, etc.).Por ejemplo, para su expresión regular:
Collection files = FileUtils.listFiles( dir, new RegexFileFilter("^(.*?)"), DirectoryFileFilter.DIRECTORY );
Esto buscará de forma recursiva archivos que coincidan con la
^(.*?)
expresión regular y devolverá los resultados como una colección.Vale la pena señalar que esto no será más rápido que ejecutar su propio código, está haciendo lo mismo: rastrear un sistema de archivos en Java es simplemente lento. La diferencia es que la versión de Apache Commons no tendrá errores.
fuente
Files.walk(Paths.get("/etc")).filter(Files::isRegularFile).collect(Collectors.toList())
En Java 8, es una vía de 1 línea
Files.find()
con una profundidad arbitrariamente grande (por ejemplo999
) yBasicFileAttributes
deisRegularFile()
public static printFnames(String sDir) { Files.find(Paths.get(sDir), 999, (p, bfa) -> bfa.isRegularFile()).forEach(System.out::println); }
Para agregar más filtrado, mejore la lambda, por ejemplo, todos los archivos jpg modificados en las últimas 24 horas:
(p, bfa) -> bfa.isRegularFile() && p.getFileName().toString().matches(".*\\.jpg") && bfa.lastModifiedTime().toMillis() > System.currentMillis() - 86400000
fuente
Este es un método recursivo muy simple para obtener todos los archivos de una raíz determinada.
Utiliza la clase Java 7 NIO Path.
private List<String> getFileNames(List<String> fileNames, Path dir) { try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { for (Path path : stream) { if(path.toFile().isDirectory()) { getFileNames(fileNames, path); } else { fileNames.add(path.toAbsolutePath().toString()); System.out.println(path.getFileName()); } } } catch(IOException e) { e.printStackTrace(); } return fileNames; }
fuente
Con Java 7, se introdujo una forma más rápida de recorrer un árbol de directorios con la funcionalidad
Paths
yFiles
. Son mucho más rápidos que la forma "antigua"File
.Este sería el código para recorrer y verificar los nombres de las rutas con una expresión regular:
public final void test() throws IOException, InterruptedException { final Path rootDir = Paths.get("path to your directory where the walk starts"); // Walk thru mainDir directory Files.walkFileTree(rootDir, new FileVisitor<Path>() { // First (minor) speed up. Compile regular expression pattern only one time. private Pattern pattern = Pattern.compile("^(.*?)"); @Override public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes atts) throws IOException { boolean matches = pattern.matcher(path.toString()).matches(); // TODO: Put here your business logic when matches equals true/false return (matches)? FileVisitResult.CONTINUE:FileVisitResult.SKIP_SUBTREE; } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes mainAtts) throws IOException { boolean matches = pattern.matcher(path.toString()).matches(); // TODO: Put here your business logic when matches equals true/false return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException { // TODO Auto-generated method stub return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException { exc.printStackTrace(); // If the root directory has failed it makes no sense to continue return path.equals(rootDir)? FileVisitResult.TERMINATE:FileVisitResult.CONTINUE; } }); }
fuente
La forma rápida de obtener el contenido de un directorio usando Java 7 NIO:
import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.FileSystems; import java.nio.file.Path; ... Path dir = FileSystems.getDefault().getPath( filePath ); DirectoryStream<Path> stream = Files.newDirectoryStream( dir ); for (Path path : stream) { System.out.println( path.getFileName() ); } stream.close();
fuente
Files.newDirectoryStream
puede lanzar una IOException. Sugiero envolver esa línea en una declaración de prueba de Java7 para que la transmisión siempre esté cerrada para usted (con excepción o no, sin la necesidad de afinally
). Véase también aquí: stackoverflow.com/questions/17739362/…La interfaz de Java para leer el contenido de las carpetas del sistema de archivos no es muy eficaz (como ha descubierto). JDK 7 corrige esto con una interfaz completamente nueva para este tipo de cosas, que debería brindar un rendimiento de nivel nativo a este tipo de operaciones.
El problema principal es que Java realiza una llamada al sistema nativo para cada archivo. En una interfaz de baja latencia, esto no es un gran problema, pero en una red con latencia incluso moderada, realmente se suma. Si perfila su algoritmo arriba, encontrará que la mayor parte del tiempo se gasta en la molesta llamada isDirectory (), eso es porque está incurriendo en un viaje de ida y vuelta por cada llamada a isDirectory (). La mayoría de los sistemas operativos modernos pueden proporcionar este tipo de información cuando se solicitó originalmente la lista de archivos / carpetas (en lugar de consultar cada ruta de archivo individual por sus propiedades).
Si no puede esperar a JDK7, una estrategia para abordar esta latencia es utilizar varios subprocesos y utilizar un ExecutorService con un número máximo de subprocesos para realizar su recursividad. No es genial (tienes que lidiar con el bloqueo de tus estructuras de datos de salida), pero será muchísimo más rápido que hacer este único subproceso.
En todas sus discusiones sobre este tipo de cosas, le recomiendo que compare con lo mejor que podría hacer usando código nativo (o incluso un script de línea de comando que hace aproximadamente lo mismo). Decir que se tarda una hora en atravesar una estructura de red no significa mucho. Decirnos que puedes hacerlo de forma nativa en 7 segundos, pero que en Java lleva una hora llamará la atención de la gente.
fuente
esto funcionará bien ... y es recursivo
File root = new File("ROOT PATH"); for ( File file : root.listFiles()) { getFilesRecursive(file); } private static void getFilesRecursive(File pFile) { for(File files : pFile.listFiles()) { if(files.isDirectory()) { getFilesRecursive(files); } else { // do your thing // you can either save in HashMap and use it as // per your requirement } } }
fuente
Personalmente, me gusta esta versión de FileUtils. Aquí hay un ejemplo que encuentra todos los mp3 o flacs en un directorio o en cualquiera de sus subdirectorios:
String[] types = {"mp3", "flac"}; Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);
fuente
Esto funcionará bien
public void displayAll(File path){ if(path.isFile()){ System.out.println(path.getName()); }else{ System.out.println(path.getName()); File files[] = path.listFiles(); for(File dirOrFile: files){ displayAll(dirOrFile); } } }
fuente
Esta función probablemente enumerará todo el nombre del archivo y su ruta desde su directorio y sus subdirectorios.
public void listFile(String pathname) { File f = new File(pathname); File[] listfiles = f.listFiles(); for (int i = 0; i < listfiles.length; i++) { if (listfiles[i].isDirectory()) { File[] internalFile = listfiles[i].listFiles(); for (int j = 0; j < internalFile.length; j++) { System.out.println(internalFile[j]); if (internalFile[j].isDirectory()) { String name = internalFile[j].getAbsolutePath(); listFile(name); } } } else { System.out.println(listfiles[i]); } } }
fuente
Java 8
public static void main(String[] args) throws IOException { Path start = Paths.get("C:\\data\\"); try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) { List<String> collect = stream .map(String::valueOf) .sorted() .collect(Collectors.toList()); collect.forEach(System.out::println); } }
fuente
Tu sentimiento está mal. Así es como funcionan los sistemas de archivos. No hay una forma más rápida (excepto cuando tiene que hacer esto repetidamente o para diferentes patrones, puede almacenar en caché todas las rutas de archivo en la memoria, pero luego debe lidiar con la invalidación de caché, es decir, lo que sucede cuando los archivos se agregan / eliminan / renombran la aplicación se ejecuta).
fuente
Para que sepas, isDirectory () es un método bastante lento. Lo encuentro bastante lento en mi navegador de archivos. Buscaré en una biblioteca para reemplazarla con código nativo.
fuente
La forma más eficiente que encontré al tratar con millones de carpetas y archivos es capturar la lista de directorios a través del comando DOS en algún archivo y analizarlo. Una vez que haya analizado los datos, podrá realizar análisis y calcular estadísticas.
fuente
import java.io.*; public class MultiFolderReading { public void checkNoOfFiles (String filename) throws IOException { File dir=new File(filename); File files[]=dir.listFiles();//files array stores the list of files for(int i=0;i<files.length;i++) { if(files[i].isFile()) //check whether files[i] is file or directory { System.out.println("File::"+files[i].getName()); System.out.println(); } else if(files[i].isDirectory()) { System.out.println("Directory::"+files[i].getName()); System.out.println(); checkNoOfFiles(files[i].getAbsolutePath()); } } } public static void main(String[] args) throws IOException { MultiFolderReading mf=new MultiFolderReading(); String str="E:\\file"; mf.checkNoOfFiles(str); } }
fuente
En Guava no tiene que esperar a que se le devuelva una colección, sino que puede iterar sobre los archivos. Es fácil imaginar una
IDoSomethingWithThisFile
interfaz en la firma de la siguiente función:public static void collectFilesInDir(File dir) { TreeTraverser<File> traverser = Files.fileTreeTraverser(); FluentIterable<File> filesInPostOrder = traverser.preOrderTraversal(dir); for (File f: filesInPostOrder) System.out.printf("File: %s\n", f.getPath()); }
TreeTraverser también le permite entre varios estilos de recorrido.
fuente
public class GetFilesRecursive { public static List <String> getFilesRecursively(File dir){ List <String> ls = new ArrayList<String>(); for (File fObj : dir.listFiles()) { if(fObj.isDirectory()) { ls.add(String.valueOf(fObj)); ls.addAll(getFilesRecursively(fObj)); } else { ls.add(String.valueOf(fObj)); } } return ls; } public static List <String> getListOfFiles(String fullPathDir) { List <String> ls = new ArrayList<String> (); File f = new File(fullPathDir); if (f.exists()) { if(f.isDirectory()) { ls.add(String.valueOf(f)); ls.addAll(getFilesRecursively(f)); } } else { ls.add(fullPathDir); } return ls; } public static void main(String[] args) { List <String> ls = getListOfFiles("/Users/srinivasab/Documents"); for (String file:ls) { System.out.println(file); } System.out.println(ls.size()); } }
fuente
Otro código optimizado
import java.io.File; import java.util.ArrayList; import java.util.List; public class GetFilesRecursive { public static List <String> getFilesRecursively(File dir){ List <String> ls = new ArrayList<String>(); if (dir.isDirectory()) for (File fObj : dir.listFiles()) { if(fObj.isDirectory()) { ls.add(String.valueOf(fObj)); ls.addAll(getFilesRecursively(fObj)); } else { ls.add(String.valueOf(fObj)); } } else ls.add(String.valueOf(dir)); return ls; } public static void main(String[] args) { List <String> ls = getFilesRecursively(new File("/Users/srinivasab/Documents")); for (String file:ls) { System.out.println(file); } System.out.println(ls.size()); } }
fuente
Un ejemplo más de listar archivos y directorios usando Java 8
filter
public static void main(String[] args) { System.out.println("Files!!"); try { Files.walk(Paths.get(".")) .filter(Files::isRegularFile) .filter(c -> c.getFileName().toString().substring(c.getFileName().toString().length()-4).contains(".jpg") || c.getFileName().toString().substring(c.getFileName().toString().length()-5).contains(".jpeg") ) .forEach(System.out::println); } catch (IOException e) { System.out.println("No jpeg or jpg files"); } System.out.println("\nDirectories!!\n"); try { Files.walk(Paths.get(".")) .filter(Files::isDirectory) .forEach(System.out::println); } catch (IOException e) { System.out.println("No Jpeg files"); } }
fuente