2009-09-03 63 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)。