Java: Đi bộ qua cây tập tin

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Bạn cần tạo ra một ứng dụng để đệ quy thăm tất cả các tập tin trong một cây tập tin? Bạn có thể thực hiện bằng cách xóa tất cả các tập tin có đuôi .class trong một cây, hoặc tìm tất cả các tập tin không được truy cập trong một năm. Bạn có thể làm như vậy với giao diện FileVisitor.

Phần này bao gồm những vấn đề sau đây:

  • Giao diện FileVisitor
  • Tiến trình Kickstarting
  • Những điều cần tính đến khi tạo một FileVisitor
  • Kiểm soát dòng chảy

Giao diện FileVisitor

Để đi bộ qua một cây tập tin, trước tiên bạn cần phải thực thi giao diện FileVisitor. Một FileVisitor quy định các hành vi cần thiết tại các điểm quan trọng trong quá trình truyền: khi một tập tin được truy cập, trước khi một thư mục được truy cập, sau khi một thư mục được truy cập, hoặc khi có sự cố xảy ra. Giao diện này có bốn phương thức tương ứng với các tình huống này:

  • preVisitDirectory - Được gọi trước các mục của một thư mục được truy cập.

  • postVisitDirectory - Được gọi sau khi tất cả các mục trong một thư mục được truy cập. Nếu gặp phải bất kỳ lỗi nào thì các ngoại lệ cụ thể được truyền cho phương thức.

  • visitFile - Được gọi trên các tập tin được truy cập. BasicFileAttributes của tập tin được truyền cho phương thức, hoặc bạn có thể sử dụng gói các thuộc tính tập tin để đọc một tập hợp cụ thể của các thuộc tính. Ví dụ, bạn có thể chọn để đọc các tập tin của DosFileAttributeView để xác định xem các tập tin có phần "ẩn" tập bit hay không.

  • visitFileFailed - Được gọi khi các tập tin không thể được truy cập. Khi đó các ngoại lệ cụ thể được truyền cho phương thức. Bạn có thể chọn để ném ngoại lệ, in nó tới console hoặc một file log.

Nếu bạn không cần phải thực hiện tất cả bốn phương thức FileVisitor, thay vì thực thi giao diện FileVisitor, bạn có thể mở rộng lớp SimpleFileVisitor. Lớp này thực thi giao diện FileVisitor, nó sẽ thăm tất cả các file trong một cây và ném ngoại lệ IOError khi gặp lỗi. Bạn có thể mở rộng lớp này và ghi đè chỉ khi nào các phương thức được bạn yêu cầu.

Dưới đây là một ví dụ dẫn xuất từ SimpleFileVisitor để in tất cả các thư mục trong một cây tập tin. Nó in các mục đầu vào là một tập tin chính quy, một liên kết tượng trưng, ​​một thư mục, hoặc một số tập tin khác mà "không xác định". Nó cũng in kích thước tính theo byte của mỗi tập tin. Bất kỳ trường hợp ngoại lệ nào khi gặp phải cũng sẽ được in ra console.

Các phương thức FileVisitor được in đậm:

import static java.nio.file.FileVisitResult.*;

public static class PrintFiles
    extends SimpleFileVisitor<Path> {

    // Print information about
    // each type of file.
    @Override
    public FileVisitResult visitFile(Path file,
                                   BasicFileAttributes attr) {
        if (attr.isSymbolicLink()) {
            System.out.format("Symbolic link: %s ", file);
        } else if (attr.isRegularFile()) {
            System.out.format("Regular file: %s ", file);
        } else {
            System.out.format("Other: %s ", file);
        }
        System.out.println("(" + attr.size() + "bytes)");
        return CONTINUE;
    }

    // Print each directory visited.
    @Override
    public FileVisitResult postVisitDirectory(Path dir,
                                          IOException exc) {
        System.out.format("Directory: %s%n", dir);
        return CONTINUE;
    }

    // If there is some error accessing
    // the file, let the user know.
    // If you don't override this method
    // and an error occurs, an IOException 
    // is thrown.
    @Override
    public FileVisitResult visitFileFailed(Path file,
                                       IOException exc) {
        System.err.println(exc);
        return CONTINUE;
    }
}

Tiến trình Kickstarting

Một khi bạn đã thực hiện FileVisitor, làm thế nào để bạn bắt đầu tập đi bộ? Có hai phương thức walkFileTree trong lớp Files.

  • walkFileTree(Path, FileVisitor)
  • walkFileTree(Path, Set<FileVisitOption>, int, FileVisitor)

Phương thức đầu tiên chỉ yêu cầu một điểm khởi đầu và một thể hiện của FileVisitor. Bạn có thể gọi tập tin truy cập PrintFiles như sau:

Path startingDir = ...;
PrintFiles pf = new PrintFiles();
Files.walkFileTree(startingDir, pf);

Phương thức walkFileTree thứ hai cho phép bạn chỉ định thêm một giới hạn về số lượng truy cập và một tập các giá trị FileVisitOption. Nếu bạn muốn đảm bảo rằng phương thức này có thể đi bộ qua toàn bộ cây tập tin, bạn có thể chỉ định Integer.MAX_VALUE cho các đối số độ sâu tối đa.

Bạn có thể chỉ định FileVisitOptionFOLLOW_LINKS để chỉ ra rằng các liên kết tượng trưng nên được lưu ý tới.

Đoạn mã này cho thấy cách mà phương thức bốn đối số có thể được gọi:

import static java.nio.file.FileVisitResult.*;

Path startingDir = ...;

EnumSet<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS);

Finder finder = new Finder(pattern);
Files.walkFileTree(startingDir, opts, Integer.MAX_VALUE, finder);

Những điều cần tính đến khi tạo một FileVisitor

Một cây tập tin được đi bộ qua mức đầu tiên, nhưng bạn không thể thực hiện bất kỳ giả định nào về trình tự lặp đi lặp lại rằng các thư mục con được viếng thăm.

Nếu chương trình của bạn sẽ thay đổi hệ thống tập tin, bạn cần phải cẩn thận xem xét cách bạn thực hiện của bạn đối với FileVisitor.

Ví dụ, nếu bạn đang viết một chương trình xóa đệ quy, thì trước tiên bạn xóa các tập tin trong một thư mục rồi sau đó mới xóa thư mục hiện tại. Trong trường hợp này, bạn xóa các thư mục trong postVisitDirectory.

Nếu bạn đang viết một chương trình copy đệ quy, thì bạn tạo thư mục mới trong preVisitDirectory trước khi cố gắng sao chép các tập tin vào nó (trong visitFiles). Nếu bạn muốn giữ các thuộc tính của thư mục nguồn (tương tự như lệnh cp-p của UNIX), bạn cần phải làm điều đó sau khi các tập tin đã được sao chép, trong postVisitDirectory.

Nếu bạn đang viết một chương trình tìm kiếm tập tin, thì bạn thực hiện các so sánh trong phương thức visitFile. Phương thức này tìm tất cả các tập tin phù hợp với tiêu chí của bạn, nhưng nó không tìm được các thư mục. Nếu bạn muốn tìm cả tập tin và thư mục, bạn cũng phải thực hiện các so sánh bằng phương thức preVisitDirectory hoặc postVisitDirectory.

Bạn cũng cần quyết định liên kết tượng trưng nào cần được quan tâm. Ví dụ, nếu bạn xóa các tập tin thì các liên kết tượng trưng có thể không được khuyến khích. Nếu bạn sao chép một cây tập tin, bạn có thể muốn cho phép nó. Theo mặc định, walkFileTree không quan tâm đến các liên kết tượng trưng.

Phương thức visitFile được gọi cho các tập tin. Nếu bạn đã xác định được tùy chọn FOLLOW_LINKS và cây tập tin của bạn có một liên kết vòng tròn vào một thư mục cha, thì việc lặp thư mục sẽ được báo cáo trong phương thức visitFileFailed với FileSystemLoopException. Đoạn mã sau đây cho thấy làm thế nào để bắt được một liên kết vòng tròn:

@Override
public FileVisitResult
    visitFileFailed(Path file,
        IOException exc) {
    if (exc instanceof FileSystemLoopException) {
        System.err.println("cycle detected: " + file);
    } else {
        System.err.format("Unable to copy:" + " %s: %s%n", file, exc);
    }
    return CONTINUE;
}

Trường hợp này có thể xảy ra chỉ khi chương trình quan tâm đến các liên kết tượng trưng.

Kiểm soát dòng chảy

Có thể bạn muốn đi bộ qua cây tập tin để tìm kiếm một thư mục cụ thể và khi tìm thấy, bạn muốn quá trình này chấm dứt. Có lẽ bạn muốn bỏ qua thư các mục cụ thể.

Phương thức FileVisitor trả về một giá trị FileVisitResult. Bạn có thể hủy bỏ quá trình đi bộ qua tập tin hoặc kiểm soát xem một thư mục có được ghé thăm bởi các giá trị bạn đã trả về trong phương thức FileVisitor hay không:

  • CONTINUE - Chỉ ra rằng nên tiếp tục đi bộ qua tập tin. Nếu phương thức preVisitDirectory trả về CONTINUE thì thư mục được truy cập.
  • TERMINATE - Ngay lập tức hủy bỏ việc đi bộ qua tập tin. Không có phương thức đi bộ qua tập tin nào được gọi sau khi giá trị này được trả về.
  • SKIP_SUBTREE - Khi preVisitDirectory trả về giá trị này, thư mục được chỉ định và thư mục con của nó bị bỏ qua, có nghĩa nhánh này được "tỉa" khỏi cây.
  • SKIP_SIBLINGS - Khi preVisitDirectory trả về giá trị này, thư mục được chỉ định sẽ không được viếng thăm, postVisitDirectory không được gọi, và không có nhánh nào tương ứng với thư mục này được viếng thăm. Nếu trả về từ phương thức postVisitDirectory, thì không có nhánh nào tương ứng. Về cơ bản, không có gì xảy ra trong thư mục chỉ định.

Trong đoạn mã này, bất kỳ thư mục nào có tên SCCS sẽ bị bỏ qua:

import static java.nio.file.FileVisitResult.*;

public FileVisitResult
     preVisitDirectory(Path dir,
         BasicFileAttributes attrs) {
    (if (dir.getFileName().toString().equals("SCCS")) {
         return SKIP_SUBTREE;
    }
    return CONTINUE;
}

Trong đoạn mã này, ngay sau khi một tập tin cụ thể được tìm thấy, tên file sẽ được in tới đầu ra chuẩn, và việc đi bộ qua tập tin sẽ kết thúc:

import static java.nio.file.FileVisitResult.*;

// The file we are looking for.
Path lookingFor = ...;

public FileVisitResult
    visitFile(Path file,
        BasicFileAttributes attr) {
    if (file.getFileName().equals(lookingFor)) {
        System.out.println("Located file: " + file);
        return TERMINATE;
    }
    return CONTINUE;
}

» Tiếp: Tìm tập tin
« Trước: Liên kết, liên kết tượng trưng hay không phải
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!