2009-09-03 62 views
7

这是我的目标。我希望能够将父目录和文件名传递给在目录和任何子目录中搜索该特定文件的方法。下面是我一直在努力的代码,但无法让它做到我想要的。它会找到我指定的文件,但不会返回任何内容。在多目录下搜索目录中的文件

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

我知道,当方法找到一个目录,并自称其重置文件变量,它是我在哪里存储找到的文件。所以这就是我得到空白回报的原因。我不知道如何实现这个目标,或者甚至是可能的。

+0

另外:你可以/应该在这里使用常规的“休息”而不是“休息”。 (@ chssPly76指出,一个普通的返回效果更好。) – 2009-09-04 06:53:12

回答

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 
} 
+0

如果该文件不存在于目录中,这不起作用,它会引发异常 – Erfan 2014-05-12 16:16:46

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文件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)。