2009-09-03 72 views
7

Đây là mục tiêu của tôi. Tôi muốn để có thể vượt qua một thư mục cha mẹ và một tên tập tin vào một phương pháp tìm kiếm tập tin cụ thể đó trong thư mục và bất kỳ thư mục con nào. Dưới đây là mã tôi đã làm việc với nhưng không thể làm cho nó làm chính xác những gì tôi muốn. Nó sẽ tìm thấy tập tin tôi chỉ định nhưng sẽ không trả lại bất cứ điều gì.Tìm kiếm tệp trong thư mục có nhiều thư mục

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

Tôi biết rằng khi phương thức tìm thấy thư mục và tự gọi nó sẽ đặt lại biến tệp là nơi tôi lưu trữ tệp đã tìm thấy. Vì vậy, đó là lý do tại sao tôi nhận được một trở lại trống. Tôi không chắc chắn làm thế nào để đạt được mục tiêu này hoặc nếu nó thậm chí có thể.

+0

Ngoài ra: bạn có thể/nên sử dụng "ngắt" thông thường thay vì "phá vỡ nhãn" tại đây. (Một trở lại đơn giản là tốt hơn vẫn như @ chssPly76 chỉ ra.) –

Trả lời

5

Vấn đề là bạn không trở về bất cứ điều gì từ cuộc gọi đệ quy:

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

tôi sẽ làm như sau:

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

Điều này không hoạt động nếu tập tin không tồn tại trong thư mục và nó sẽ tăng ngoại lệ – Erfan

1

Trong thực tế có rất nhiều giải pháp để thực hiện công việc. Tôi giả sử rằng bạn muốn tìm một tệp duy nhất (hoặc cái đầu tiên) được tìm thấy trong cây thư mục khớp với tên tệp. Đó là vấn đề tối ưu hóa vì có nhiều cách để khám phá giải pháp và chúng tôi muốn tìm một giải pháp có thể chấp nhận được.

1- Giải pháp sử dụng 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; 
} 

Giải pháp sử dụng thư viện FileUtils không phải là một giải pháp phù hợp vì phương pháp này FileUtils#listFiles() tải tất cả các cây thư mục/thư mục (chi phí là đắt!). Chúng ta không cần phải biết tất cả các cây, chúng ta có thể chọn một thuật toán tốt hơn mà dừng lại khi tập tin được tìm thấy.

2- Recursive Giải pháp

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

Các thử nghiệm thuật toán nếu tập tin tồn tại bên trong bất kỳ thư mục. Nếu không, nó sẽ cố gắng thư mục con của thư mục hiện tại ... đệ quy. Nếu tệp không được tìm thấy trong nhánh hiện tại, nó sẽ thử một thư mục con khác.

Việc khám phá rất sâu và đối với bất kỳ tệp nào ở độ sâu của 1 thuật toán sẽ khám phá toàn bộ các thư mục con trước đó (các nhánh trước được khám phá hoàn toàn!). Thuật toán này có hiệu suất tốt nhất cho các tệp ở vị trí sâu bên trong nhánh đầu tiên.

Trong phần lớn các trường hợp, vị trí tệp không sâu, vì vậy hãy khám phá một thuật toán khác hoạt động trong hầu hết các trường hợp.

3 nhanh nhất Giải pháp: thăm dò bởi độ sâu

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

Đối với mỗi độ sâu, thuật toán tìm kiếm các tập tin bên trong tất cả các thư mục cùng cấp. Nếu tập tin không được tìm thấy, nó sẽ cố gắng cấp độ tiếp theo (độ sâu ++). Do sự thăm dò song song (đối xứng), giải pháp này phù hợp trong hầu hết các trường hợp.

So sánh:

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

Kết quả:

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


[EDIT] Bạn cũng có thể sử dụng Java 8 tập tin API để làm công việc này:

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

Nhưng thời gian thực hiện là không tốt (994ms).

Các vấn đề liên quan