在具有多个目录的目录中搜索文件

时间:2009-09-03 20:23:51

标签: java

这是我的目标。我希望能够将父目录和文件名传递给在目录和任何子目录中搜索该特定文件的方法。下面是我一直在使用的代码,但无法让它完全按照我的意愿去做。它会找到我指定的文件,但不会返回任何内容。

private static File findFile(File dir, String name) {
    String file     = "";
    File[] dirlist  = dir.listFiles();

    search:
        for(int i = 0; i < dirlist.length; i++) {
            if(dirlist[i].isDirectory()) {
                findFile(dirlist[i], name);
            } else if(dirlist[i].getName().matches(name)) {
                file = dirlist[i].toString();
                break search;
            }
        }

    return new File(file);
}

我知道当方法找到目录并调用自身时,它会重置文件变量,这是我存储找到的文件的位置。所以这就是为什么我得到一个空白的回报。我不确定如何实现这一目标,或者甚至是否可能。

2 个答案:

答案 0 :(得分:5)

问题是你没有从递归调用中返回任何内容:

if(dirlist[i].isDirectory()) {
    findFile(dirlist[i], name); // <-- here
} else if(dirlist[i].getName().matches(name)) {

我会做以下事情:

private static File findFile(File dir, String name) {
  File result = null; // no need to store result as String, you're returning File anyway
  File[] dirlist  = dir.listFiles();

  for(int i = 0; i < dirlist.length; i++) { 
    if(dirlist[i].isDirectory()) {
      result = findFile(dirlist[i], name);
      if (result!=null) break; // recursive call found the file; terminate the loop
    } else if(dirlist[i].getName().matches(name)) {
      return dirlist[i]; // found the file; return it
    }
  }
  return result; // will return null if we didn't find anything
}

答案 1 :(得分:1)

事实上,有很多解决方案可以完成这项工作。 我假设您要在目录树中找到与fileName匹配的 唯一文件 (或第一个)。 这是一个优化问题,因为有多种方法可以探索解决方案,我们希望找到一个可接受的解决方案。

1-使用FileUtils.listFiles的解决方案

public static File searchFileWithFileUtils(final File file, final String fileName) {
    File target = null;
    if(file.isDirectory()) {
        Collection<File> files = FileUtils.listFiles(file, null, true);
        for (File currFile : files) {
            if (currFile.isFile() && currFile.getName().equals(fileName)) {
                target = currFile;
                break;
            }
        }
    }
    return target;
}

使用库FileUtils的解决方案不是一个合适的解决方案,因为方法FileUtils#listFiles()加载了所有目录/文件夹树(成本很高!)。 我们不需要知道所有的树,我们可以选择一个更好的算法,在找到文件时停止。

2-递归解决方案

public static File searchFileRecursive(final File file, final String search) {
    if (file.isDirectory()) {
        File[] files = file.listFiles();
        for (File f : files) {
            File target = searchFileRecursive(f, search);
            if(target != null) {
                return target;
            }
        }
    } else {
        if (search.equals(file.getName())) {
            return file;
        }
    }
    return null;
}

算法测试文件是否存在于任何文件夹中。如果没有,它会递归地尝试当前文件夹的子文件夹。如果在当前分支中找不到该文件,则会尝试另一个子文件夹。

探索是深入的,对于任何深度为1的文件,算法将探索以前的整个子文件夹(以前的分支是完全探索的!)。 该算法对第一个分支内部深处的文件具有最佳性能。

在大多数情况下,文件位置不深,所以让我们探索另一种适用于大多数情况的算法。

3-最快的解决方案:深度探索

public static File searchFileByDeepness(final String directoryName, final String fileName) {
    File target = null;
    if(directoryName != null && fileName != null) {
        File directory = new File(directoryName);
        if(directory.isDirectory()) {
            File file = new File(directoryName, fileName);
            if(file.isFile()) {
                target = file;
            }
            else {
                List<File> subDirectories = getSubDirectories(directory);
                do {
                    List<File> subSubDirectories = new ArrayList<File>();
                    for(File subDirectory : subDirectories) {
                        File fileInSubDirectory = new File(subDirectory, fileName);
                        if(fileInSubDirectory.isFile()) {
                            return fileInSubDirectory;
                        }
                        subSubDirectories.addAll(getSubDirectories(subDirectory));
                    }
                    subDirectories = subSubDirectories;
                } while(subDirectories != null && ! subDirectories.isEmpty());
            }
        }
    }
    return target;
}

private static List<File> getSubDirectories(final File directory) {
    File[] subDirectories = directory.listFiles(new FilenameFilter() {
        @Override
        public boolean accept(final File current, final String name) {
            return new File(current, name).isDirectory();
        }
    });
    return Arrays.asList(subDirectories);
}

对于每个深度,算法搜索同一级别的所有文件夹中的文件。如果找不到该文件,它将尝试下一级(深度++)。 由于平行探索(对称性),该解决方案适用于大多数情况。

<强>比较

public class FileLocationFinder {

    public static void main(final String[] args) {
        String rootFolder = args[0];
        String fileName = args[1];

        long start = System.currentTimeMillis();
        File target = searchFileWithFileUtils(new File(rootFolder), fileName);
        System.out.println(target.getAbsolutePath());
        System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");

        start = System.currentTimeMillis();
        target = searchFileRecursive(new File(rootFolder), fileName);
        System.out.println(target.getAbsolutePath());
        System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");

        start = System.currentTimeMillis();
        target = searchFileByDeepness(rootFolder, fileName);
        System.out.println(target.getAbsolutePath());
        System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");
    }


    // Solution with FileUtils#listFiles
    //--------------------------------------------

    public static File searchFileWithFileUtils(final File file, final String fileName) {
        File target = null;
        if(file.isDirectory()) {
            Collection<File> files = FileUtils.listFiles(file, null, true);
            for (File currFile : files) {
                if (currFile.isFile() && currFile.getName().equals(fileName)) {
                    target = currFile;
                    break;
                }
            }
        }
        return target;
    }


    // Recursive solution
    //--------------------------------------------

    public static File searchFileRecursive(final File file, final String search) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                File target = searchFileRecursive(f, search);
                if(target != null) {
                    return target;
                }
            }
        } else {
            if (search.equals(file.getName())) {
                return file;
            }
        }
        return null;
    }


    // Fastest solution
    //--------------------------------------------

    public static File searchFileByDeepness(final String directoryName, final String fileName) {
        File target = null;
        if(directoryName != null && fileName != null) {
            File directory = new File(directoryName);
            if(directory.isDirectory()) {
                File file = new File(directoryName, fileName);
                if(file.isFile()) {
                    target = file;
                }
                else {
                    List<File> subDirectories = getSubDirectories(directory);
                    do {
                        List<File> subSubDirectories = new ArrayList<File>();
                        for(File subDirectory : subDirectories) {
                            File fileInSubDirectory = new File(subDirectory, fileName);
                            if(fileInSubDirectory.isFile()) {
                                return fileInSubDirectory;
                            }
                            subSubDirectories.addAll(getSubDirectories(subDirectory));
                        }
                        subDirectories = subSubDirectories;
                    } while(subDirectories != null && ! subDirectories.isEmpty());
                }
            }
        }
        return target;
    }

    private static List<File> getSubDirectories(final File directory) {
        File[] subDirectories = directory.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(final File current, final String name) {
                return new File(current, name).isDirectory();
            }
        });
        return Arrays.asList(subDirectories);
    }
}

结果:

  

searchFileWithFileUtils:20186ms | searchFileRecursive:1134ms | searchFileByDeepness:16ms

<强> [编辑] 您还可以使用Java 8 Files API完成此任务:

public static File searchFileJava8(final String rootFolder, final String fileName) {
    File target = null;
    Path root = Paths.get(rootFolder);
    try (Stream<Path> stream = Files.find(root, Integer.MAX_VALUE, (path, attr) ->
            path.getFileName().toString().equals(fileName))) {
        Optional<Path> path = stream.findFirst();
        if(path.isPresent()) {
            target = path.get().toFile();
        }
    }
    catch (IOException e) {
    }
    return target;
}

但执行时间不是更好(994ms)。