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

时间:2010-03-28 21:26:33

标签: 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());
    }
  }
}

这只是后来的一个测试,我不打算使用这样的代码,相反,我将添加与高级正则表达式匹配的每个文件的路径和修改日期。

19 个答案:

答案 0 :(得分:127)

假设这是您正在编写的实际生产代码,那么我建议使用解决方案来解决已经解决的问题 - Apache Commons IO,特别是FileUtils.listFiles()。它处理嵌套目录,过滤器(基于名称,修改时间等)。

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

Collection files = FileUtils.listFiles(
  dir, 
  new RegexFileFilter("^(.*?)"), 
  DirectoryFileFilter.DIRECTORY
);

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

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

答案 1 :(得分:54)

在Java 8中,它是一个通过Files.find()的1-liner,具有任意大的深度(例如999)和BasicFileAttributes isRegularFile()

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

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

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

答案 2 :(得分:25)

这是一个非常简单的递归方法,可以从给定的根中获取所有文件。

它使用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;
} 

答案 3 :(得分: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;
        }
    });
}

答案 4 :(得分: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();

答案 5 :(得分:12)

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

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

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

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

答案 6 :(得分: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
        }
    }
}

答案 7 :(得分:3)

我个人喜欢这个版本的FileUtils。这是一个查找目录或其任何子目录中的所有mp3或flacs的示例:

String[] types = {"mp3", "flac"};
Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);

答案 8 :(得分:2)

这样可以正常使用

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);
        }
    }
}

答案 9 :(得分:1)

此函数可能会列出其目录及其子目录中的所有文件名及其路径。

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]);
        }

    }

}

答案 10 :(得分:0)

使用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");
        }
}

答案 11 :(得分:0)

另一个优化的代码

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());
    }
}

答案 12 :(得分:0)

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());
    }
}

答案 13 :(得分:0)

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);
        }


    }

答案 14 :(得分:0)

在Guava中,您不必等待Collection返回给您,但实际上可以迭代文件。很容易想象下面函数签名中的IDoSomethingWithThisFile接口:

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还允许您在各种遍历样式之间。

答案 15 :(得分:0)

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);
   }
}

答案 16 :(得分:0)

我在处理数百万个文件夹和文件时发现的更有效的方法是通过DOS命令在某个文件中捕获目录列表并解析它。一旦解析了数据,就可以进行分析和计算统计数据。

答案 17 :(得分:0)

就像你知道isDirectory()是一个非常慢的方法。我在文件浏览器中发现它很慢。我将查看一个库,用本机代码替换它。

答案 18 :(得分:0)

  感觉它是愚蠢的访问   filesystem并获取内容   每个子目录而不是获取   一切都在。

你的感觉是对的。这就是文件系统的工作方式。没有更快的方法(除非您必须重复执行此操作或针对不同的模式,您可以将所有文件路径缓存在内存中,但是您必须处理缓存失效,即在添加/删除/重命名文件时会发生什么情况应用程序运行。)