Làm cách nào để lặp qua các tệp trong một thư mục trong Java?


175

Tôi cần lấy danh sách tất cả các tệp trong một thư mục, bao gồm các tệp trong tất cả các thư mục con. Cách tiêu chuẩn để thực hiện việc lặp thư mục với Java là gì?

Câu trả lời:


207

Bạn có thể sử dụng File#isDirectory()để kiểm tra nếu tệp đã cho (đường dẫn) là một thư mục. Nếu đây là true, sau đó bạn chỉ cần gọi lại phương thức tương tự với File#listFiles()kết quả của nó . Điều này được gọi là đệ quy .

Đây là một ví dụ khởi động cơ bản.

public static void main(String... args) {
    File[] files = new File("C:/").listFiles();
    showFiles(files);
}

public static void showFiles(File[] files) {
    for (File file : files) {
        if (file.isDirectory()) {
            System.out.println("Directory: " + file.getName());
            showFiles(file.listFiles()); // Calls same method again.
        } else {
            System.out.println("File: " + file.getName());
        }
    }
}

Lưu ý rằng điều này nhạy cảm StackOverflowErrorkhi cây sâu hơn ngăn xếp của JVM có thể giữ. Thay vào đó, bạn có thể muốn sử dụng phương pháp lặp hoặc đệ quy đuôi , nhưng đó là một chủ đề khác;)


cảm ơn Balus, có ý tưởng nào về độ sâu có thể như một phỏng đoán chung không?
James

10
Phụ thuộc vào cài đặt bộ nhớ JVM của bạn. Nhưng nói chung là một cái gì đó như một vài ngàn. Nếu bạn nghĩ rằng bạn có thể đã từng chạy vào một thư mục như vậy, thì đừng sử dụng đệ quy.
Mike Baranczak

4
Điều này dễ bị ảnh hưởng NullPointerExceptionkhi hệ thống tệp thay đổi giữa cuộc gọi đến isDirectorylistFilescó thể xảy ra nếu System.out.printlncác khối hoặc bạn thực sự không may mắn. Kiểm tra xem đầu ra listFileskhông phải là null sẽ giải quyết điều kiện cuộc đua đó.
Mike Samuel

1
@BoratSagdiyev, Không sử dụng API tệp Java cũ, nhưng nếu bạn đang sử dụng JVM hiện đại thì java.nio.file.DirectoryStreamcho phép bạn lặp lại qua một thư mục và có thể được triển khai để có dấu chân bộ nhớ nhỏ nhưng cách duy nhất để biết chắc chắn sẽ là để theo dõi việc sử dụng bộ nhớ trên một nền tảng cụ thể.
Mike Samuel

1
Thư mục "C: \\" không phải là lựa chọn tốt nhất của một ví dụ)
Vyacheslav

86

Nếu bạn đang sử dụng Java 1.7, bạn có thể sử dụng java.nio.file.Files.walkFileTree(...).

Ví dụ:

public class WalkFileTreeExample {

  public static void main(String[] args) {
    Path p = Paths.get("/usr");
    FileVisitor<Path> fv = new SimpleFileVisitor<Path>() {
      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
          throws IOException {
        System.out.println(file);
        return FileVisitResult.CONTINUE;
      }
    };

    try {
      Files.walkFileTree(p, fv);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

Nếu bạn đang sử dụng Java 8, bạn có thể sử dụng giao diện luồng với java.nio.file.Files.walk(...):

public class WalkFileTreeExample {

  public static void main(String[] args) {
    try (Stream<Path> paths = Files.walk(Paths.get("/usr"))) {
      paths.forEach(System.out::println);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

1
Có cách nào với các luồng để đặt một điểm kiểm tra khi một thư mục mới được đi và thực hiện một chức năng không?
Raghu DV

28

Hãy xem lớp FileUtils trong Apache Commons - cụ thể là iterateFiles :

Cho phép lặp lại các tệp trong thư mục đã cho (và tùy chọn thư mục con của nó).


5
API này không thực sự phát trực tuyến (nếu bạn quan tâm đến việc sử dụng mem), trước tiên, nó tạo ra một bộ sưu tập, chỉ sau đó trả về một trình vòng lặp qua nó: return listFiles (thư mục, fileFilter, dirFilter) .iterator ();
Gili Nachum

Tùy chọn tốt cho Java 1.6.
David I.

Đồng ý với @GiliNachum. FileUtils của Apache trước tiên thu thập tất cả các tệp và đưa ra một trình vòng lặp cho chúng. Nó có hại cho tài nguyên nếu bạn có một lượng lớn tệp.
Samdanos

8

Đối với Java 7+, cũng có https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html

Ví dụ lấy từ Javadoc:

List<Path> listSourceFiles(Path dir) throws IOException {
   List<Path> result = new ArrayList<>();
   try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.{c,h,cpp,hpp,java}")) {
       for (Path entry: stream) {
           result.add(entry);
       }
   } catch (DirectoryIteratorException ex) {
       // I/O error encounted during the iteration, the cause is an IOException
       throw ex.getCause();
   }
   return result;
}

8

Sử dụng org.apache.commons.io.FileUtils

File file = new File("F:/Lines");       
Collection<File> files = FileUtils.listFiles(file, null, true);     
for(File file2 : files){
    System.out.println(file2.getName());            
} 

Sử dụng false nếu bạn không muốn tập tin từ thư mục con.


3

Đó là một cái cây, vì vậy đệ quy là bạn của bạn: bắt đầu với thư mục mẹ và gọi phương thức để lấy một mảng các tệp con. Lặp lại qua mảng con. Nếu giá trị hiện tại là một thư mục, hãy chuyển nó đến một cuộc gọi đệ quy của phương thức của bạn. Nếu không, xử lý tệp lá thích hợp.


2

Như đã lưu ý, đây là một vấn đề đệ quy. Đặc biệt, bạn có thể muốn xem xét

listFiles() 

Trong API tệp java tại đây . Nó trả về một mảng của tất cả các tệp trong một thư mục. Sử dụng cái này cùng với

isDirectory()

để xem nếu bạn cần tái diễn thêm là một khởi đầu tốt.


Liên kết này có thể được sử dụng vì một trong câu trả lời bị hỏng.
Donglecow

0

Để thêm bằng câu trả lời @msandiford, vì hầu hết các lần khi một cây tệp được đi, bạn có thể muốn thực thi một chức năng như một thư mục hoặc bất kỳ tệp cụ thể nào được truy cập. Nếu bạn miễn cưỡng sử dụng các luồng. Các phương pháp sau đây được ghi đè có thể được thực hiện

Files.walkFileTree(Paths.get(Krawl.INDEXPATH), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
    new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException {
                // Do someting before directory visit
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException {
                // Do something when a file is visited
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                throws IOException {
                // Do Something after directory visit 
                return FileVisitResult.CONTINUE;
        }
});

0

Bạn cũng có thể sử dụng sai File.list (FilenameFilter) (và các biến thể) để truyền tải tệp. Mã ngắn và hoạt động trong các phiên bản java đầu tiên, ví dụ:

// list files in dir
new File(dir).list(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        String file = dir.getAbsolutePath() + File.separator + name;
        System.out.println(file);
        return false;
    }
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.