Eu tenho essa função que imprime o nome de todos os arquivos em um diretório recursivamente. O problema é que meu código é muito lento porque precisa acessar um dispositivo de rede remoto a cada iteração.
Meu plano é primeiro carregar todos os arquivos do diretório recursivamente e depois passar por todos os arquivos com o regex para filtrar todos os arquivos que não quero. Alguém tem uma sugestão melhor?
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());
}
}
}
Este é apenas um teste posterior. Não vou usar o código como este, em vez disso, vou adicionar o caminho e a data de modificação de cada arquivo que corresponde a um regex avançado a um array.
Respostas:
Presumindo que este seja o código de produção real que você estará escrevendo, sugiro usar a solução para esse tipo de coisa que já foi resolvida - Apache Commons IO , especificamente
FileUtils.listFiles()
. Ele lida com diretórios aninhados, filtros (com base no nome, hora de modificação, etc).Por exemplo, para seu regex:
Collection files = FileUtils.listFiles( dir, new RegexFileFilter("^(.*?)"), DirectoryFileFilter.DIRECTORY );
Isso irá pesquisar recursivamente por arquivos que correspondam ao
^(.*?)
regex, retornando os resultados como uma coleção.É importante notar que isso não será mais rápido do que lançar seu próprio código, está fazendo a mesma coisa - vasculhar um sistema de arquivos em Java é lento. A diferença é que a versão Apache Commons não terá bugs.
fonte
Files.walk(Paths.get("/etc")).filter(Files::isRegularFile).collect(Collectors.toList())
No Java 8, é uma via de 1 linha
Files.find()
com uma profundidade arbitrariamente grande (por exemplo999
) eBasicFileAttributes
deisRegularFile()
public static printFnames(String sDir) { Files.find(Paths.get(sDir), 999, (p, bfa) -> bfa.isRegularFile()).forEach(System.out::println); }
Para adicionar mais filtragem, aprimore o lambda, por exemplo, todos os arquivos jpg modificados nas últimas 24 horas:
(p, bfa) -> bfa.isRegularFile() && p.getFileName().toString().matches(".*\\.jpg") && bfa.lastModifiedTime().toMillis() > System.currentMillis() - 86400000
fonte
Este é um método recursivo muito simples para obter todos os arquivos de uma determinada raiz.
Ele usa a classe 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; }
fonte
Com o Java 7, uma maneira mais rápida de percorrer uma árvore de diretórios foi introduzida com a funcionalidade
Paths
eFiles
. Eles são muito mais rápidos do que oFile
método "antigo" .Este seria o código para percorrer e verificar os nomes dos caminhos com uma expressão 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; } }); }
fonte
A maneira mais rápida de obter o conteúdo de um diretório 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();
fonte
Files.newDirectoryStream
pode lançar uma IOException. Eu sugiro envolver essa linha em uma instrução try-with-Java7 para que o fluxo sempre seja fechado para você (exceção ou não, sem a necessidade de afinally
). Veja também aqui: stackoverflow.com/questions/17739362/…A interface do Java para ler o conteúdo da pasta do sistema de arquivos não tem muito desempenho (como você descobriu). O JDK 7 corrige isso com uma interface completamente nova para esse tipo de coisa, que deve trazer desempenho de nível nativo para esses tipos de operações.
O problema principal é que o Java faz uma chamada de sistema nativo para cada arquivo. Em uma interface de baixa latência, isso não é um grande problema - mas em uma rede com latência moderada, isso realmente faz sentido. Se você criar o perfil de seu algoritmo acima, verá que a maior parte do tempo é gasta na chata isDirectory () - isso porque você está incorrendo em uma viagem de ida e volta para cada chamada para isDirectory (). A maioria dos sistemas operacionais modernos pode fornecer esse tipo de informação quando a lista de arquivos / pastas foi solicitada originalmente (ao contrário de consultar cada caminho de arquivo individual para suas propriedades).
Se você não puder esperar pelo JDK7, uma estratégia para lidar com essa latência é usar vários threads e usar um ExecutorService com no máximo # de threads para realizar sua recursão. Não é ótimo (você tem que lidar com o bloqueio de suas estruturas de dados de saída), mas será muito mais rápido do que fazer esse único thread.
Em todas as suas discussões sobre esse tipo de coisa, eu recomendo fortemente que você compare com o melhor que você poderia fazer usando código nativo (ou mesmo um script de linha de comando que faz quase a mesma coisa). Dizer que leva uma hora para percorrer uma estrutura de rede não significa muito. Dizendo que você pode fazer isso nativo em 7 segundos, mas leva uma hora em Java vai chamar a atenção das pessoas.
fonte
isso vai funcionar bem ... e é 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 } } }
fonte
Eu pessoalmente gosto dessa versão do FileUtils. Aqui está um exemplo que encontra todos os mp3s ou flacs em um diretório ou qualquer um de seus subdiretórios:
String[] types = {"mp3", "flac"}; Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);
fonte
Isso vai funcionar bem
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); } } }
fonte
Esta função provavelmente irá listar todo o nome do arquivo e seu caminho de seu diretório e seus subdiretórios.
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]); } } }
fonte
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); } }
fonte
Seu sentimento está errado. É assim que os sistemas de arquivos funcionam. Não há maneira mais rápida (exceto quando você tem que fazer isso repetidamente ou para padrões diferentes, você pode armazenar em cache todos os caminhos de arquivo na memória, mas então você tem que lidar com a invalidação do cache, ou seja, o que acontece quando os arquivos são adicionados / removidos / renomeados enquanto o aplicativo é executado).
fonte
Só para você saber, isDirectory () é um método muito lento. Estou achando muito lento no meu navegador de arquivos. Estarei procurando em uma biblioteca para substituí-la por código nativo.
fonte
A maneira mais eficiente que encontrei para lidar com milhões de pastas e arquivos é capturar a lista de diretórios por meio do comando DOS em algum arquivo e analisá-la. Depois de analisar os dados, você pode fazer análises e computar estatísticas.
fonte
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); } }
fonte
No Guava, você não precisa esperar que uma coleção seja devolvida a você, mas pode realmente iterar os arquivos. É fácil imaginar uma
IDoSomethingWithThisFile
interface na assinatura da função abaixo: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 também permite que você entre vários estilos de passagem.
fonte
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()); } }
fonte
Outro código otimizado
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()); } }
fonte
Mais um exemplo de listagem de arquivos e diretórios 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"); } }
fonte