使用Java递归列出目录中的所有文件

Hul*_*ner 80 java file-io

我有这个函数以递归方式打印目录中的所有文件的名称.问题是我的代码非常慢,因为它必须在每次迭代时访问远程网络设备.

我的计划是先递归加载目录中的所有文件,然后再使用正则表达式遍历所有文件,过滤掉我不想要的所有文件.有人有更好的建议吗?

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());
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这只是稍后的一个测试,我不打算使用这样的代码,而是我要将每个匹配高级正则表达式的文件的路径和修改日期添加到数组中.

ska*_*man 130

假设这是实际的生产代码,你会写,那么我建议使用解决这样的事情这已经得到解决- 阿帕奇百科全书IO,具体FileUtils.listFiles().它处理嵌套目录,过滤器(基于名称,修改时间等).

例如,对于你的正则表达式:

Collection files = FileUtils.listFiles(
  dir, 
  new RegexFileFilter("^(.*?)"), 
  DirectoryFileFilter.DIRECTORY
);
Run Code Online (Sandbox Code Playgroud)

这将递归搜索与^(.*?)正则表达式匹配的文件,并将结果作为集合返回.

值得注意的是,这并不比滚动自己的代码快,它做同样的事情 - 用Java搜索文件系统的速度很慢.不同的是,Apache Commons版本中没有任何错误.

  • @ hanzallah-afgan:问题和答案都超过5年.在此期间发布了两个主要的Java版本,因此您可能不会研究Java 7 NIO等新功能. (5认同)
  • 如果您知道并接受性能影响,请仅使用FileUtils:https://github.com/brettryan/io-recurse-tests.原生java8替代方案允许简洁且更有效的表示法,例如:`Files.walk(Paths.get("/ etc")).filter(Files :: isRegularFile).collect(Collectors.toList())` (4认同)

Boh*_*ian 59

在Java 8,它是通过1衬垫Files.find()与一个任意大的深度(例如999)和BasicFileAttributesisRegularFile()

public static printFnames(String sDir) {
    Files.find(Paths.get(sDir), 999, (p, bfa) -> bfa.isRegularFile()).forEach(System.out::println);
}
Run Code Online (Sandbox Code Playgroud)

要添加更多过滤,请增强lambda,例如在过去24小时内修改的所有jpg文件:

(p, bfa) -> bfa.isRegularFile()
  && p.getFileName().toString().matches(".*\\.jpg")
  && bfa.lastModifiedTime().toMillis() > System.currentMillis() - 86400000
Run Code Online (Sandbox Code Playgroud)

  • 我建议始终使用那些在try-with-resources块中返回Stream的Files方法:否则,您将保持资源打开状态 (2认同)

Dan*_*Dan 26

这是一个非常简单的递归方法,用于从给定的根获取所有文件.

它使用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;
} 
Run Code Online (Sandbox Code Playgroud)


jbo*_*boi 18

使用Java 7,通过PathsFiles功能引入了更快的通过目录树行走的方式.它们比"旧" File方式快得多.

这将是通过正则表达式遍历和检查路径名称的代码:

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;
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案:),还有一个名为"SimpleFileVisitor"的实现类,如果你不需要所有实现的功能,你可以直接覆盖所需的功能. (5认同)

Rea*_*wTo 13

使用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();
Run Code Online (Sandbox Code Playgroud)

  • 很好,但只获取一个目录的文件.如果您想查看所有子目录,请参阅我的替代答案. (3认同)
  • `Files.newDirectoryStream`可以抛出IOException.我建议在Java7 try-with-statement中包装该行,以便始终为您关闭流(异常与否,不需要`finally`).另见:http://stackoverflow.com/questions/17739362/java7-try-with-resources-statement-advantage (3认同)

Kev*_*Day 12

Java用于读取文件系统文件夹内容的界面性能不是很高(正如您所发现的那样).JDK 7通过一个全新的接口来解决这个问题,它应该为这些类型的操作带来本机级别的性能.

核心问题是Java为每个文件进行本机系统调用.在低延迟接口上,这不是什么大不了的事 - 但在具有中等延迟的网络上,它真的会增加.如果您对上面的算法进行了分析,您会发现大部分时间花在了烦人的isDirectory()调用上 - 这是因为您每次调用isDirectory()都会产生一次往返.大多数现代操作系统可以在最初请求文件/文件夹列表时提供此类信息(而不是查询每个文件路径的属性).

如果您不能等待JDK7,那么解决此延迟的一种策略是使用多线程并使用具有最多线程数的ExecutorService来执行递归.它不是很好(你必须处理输出数据结构的锁定),但它比这个单线程更快.

在您关于此类事情的所有讨论中,我强烈建议您使用本机代码(甚至是执行大致相同操作的命令行脚本)进行比较.说遍历网络结构需要一个小时并不是真的意味着那么多.告诉我们你可以在7秒内完成原生,但是用Java需要一个小时才会引起人们的注意.

  • @Martin [这些是您正在寻找的文档.](http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#walkFileTree%28java.nio.file .路径,%20java.util.Set,%20int,%20java.nio.file.FileVisitor%29) (4认同)
  • Java 7现在已经存在,所以如何在Java 7中执行它将有所帮助.或者至少是一个链接.或者在谷歌上搜索的类名. - 这毕竟是«stackoverflow»而不是«理论cs»;-). (3认同)
  • 好吧,让我们看看...我原来的帖子是在2010年3月......现在是2012年1月......我刚刚检查了我的设备库存历史,我没有看到自己在2010年3月有一台时间机器,所以我想我可能在没有给出明确的例子的情况下回答是合理的;-) (3认同)

Pra*_*ant 5

这将工作得很好...和它的递归

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)