Java: Tìm tập tin
Nếu bạn đã từng sử dụng kịch bản shell, bạn có nhiều khả năng sử dụng tương thích mẫu để xác định vị trí các tập tin. Trong thực tế, bạn đã có thể sử dụng nó rộng rãi. Nếu bạn không sử dụng nó, thì bạn sử dụng mô hình kết hợp sử dụng ký tự đặc biệt để tạo ra một mô hình và sau đó tìm tên tập tin có thể được so sánh với mô hình đó. Ví dụ, trong hầu hết các kịch bản shell thì dấu hoa thị *
tương ứng với bất kỳ số lượng ký tự nào. Ví dụ, câu lệnh sau đây liệt kê tất cả các tập tin trong thư mục hiện tại kết thúc bằng .html
:
% Ls * .html
Gói java.nio.file
gói cung cấp việc hỗ trợ chương trình cho tính năng hữu ích này. Mỗi sự thực thi hệ thống tập tin cung cấp một PathMatcher
. Bạn có thể lấy một PathMatcher của hệ thống tập tin bằng phương thức getPathMatcher(String)
trong lớp FileSystem
. Đoạn mã sau lấy về bộ tương thích đường dẫn cho hệ thống tập tin mặc định:
String pattern = ...;
PathMatcher matcher =
FileSystems.getDefault().getPathMatcher("glob:" + pattern);
Đối số chuỗi được truyền tới getPathMatcher
sẽ xác định cú pháp và mô hình để tương thích. Ví dụ trên nói về cú pháp glob. Nếu bạn không quen với cú pháp glob, xin xem phần Glob là gì?.
Cú pháp Glob là dễ sử dụng và linh hoạt, nhưng nếu bạn thích bạn cũng có thể sử dụng biểu thức thông thường, hoặc biểu thức chính quy regex.
Nếu bạn muốn sử dụng một số hình thức khác của chuỗi dựa trên tương thích mẫu, bạn có thể tạo lớp PathMatcher của
riêng bạn. Các ví dụ trong trang này cú pháp sử dụng glob.
Một khi bạn đã tạo ra thể hiện PathMatcher
của bạn, bạn đã sẵn sàng để tương thích với các tập tin ngược với nó. Interface PathMatcher
có một phương thức duy nhất là matches, nó có đối số là Path và trả về một giá trị boolea thể hiện sự tương thích mẫu hoặc không. Đoạn mã sau đây sẽ tìm kiếm các tập tin kết thúc bằng .java
hoặc .class
và in các tập tin đó tới đầu ra chuẩn:
PathMatcher matcher =
FileSystems.getDefault().getPathMatcher("glob:*.{java,class}");
Path filename = ...;
if (matcher.matches(filename)) {
System.out.println(filename);
}
Tương thích mẫu đệ quy
Tìm kiếm các tập tin tương thích với mẫu thường song hành với việc Đi bộ qua cây tập tin. Có thể bạn muốn tìm kiếm vị trí của một tập tin trên hệ thống tập tin, hoặc bạn cần tìm tất cả các file trong một cây tập tin có phần mở rộng cụ thể nào đó.
Ví dụ Find sau đây tương tự như tiện ích find của UNIX, tất nhiên là còn thiếu một vài chức năng. Bạn có thể mở rộng ví dụ này để đưa thêm các chức năng khác bạn muốn, chẳng hạn như tiện ích find của UNIX hỗ trợ cờ -prune
để loại trừ toàn bộ cây con khi tìm kiếm. Bạn có thể thực hiện chức năng đó bằng cách trả về SKIP_SUBTREE
trong phương thức preVisitDirectory
. Để thực hiện tùy chọn -L
sau liên kết tượng trưng, bạn có thể sử dụng phương thức bốn đối số walkFileTree
và truyền bằng enum FOLLOW_LINKS
(nhưng bạn cần kiểm thử xem có liên kết vòng tròn không bằng phương thức visitFile
).
Để chạy ứng dụng Find, sử dụng định dạng sau:
% java Find <path> -name "<glob_pattern>"
Mẫu được đặt trong dấu ngoặc kép nên bất kỳ ký tự nào đều không được thông dịch bởi shell. Ví dụ:
% java Find . -name "*.html"
Đây là mã nguồn của ví dụ Find
:
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import static java.nio.file.FileVisitResult.*;
import static java.nio.file.FileVisitOption.*;
import java.util.*;
public class Find {
public static class Finder
extends SimpleFileVisitor<Path> {
private final PathMatcher matcher;
private int numMatches = 0;
Finder(String pattern) {
matcher = FileSystems.getDefault()
.getPathMatcher("glob:" + pattern);
}
// Compares the glob pattern against
// the file or directory name.
void find(Path file) {
Path name = file.getFileName();
if (name != null && matcher.matches(name)) {
numMatches++;
System.out.println(file);
}
}
// Prints the total number of
// matches to standard out.
void done() {
System.out.println("Matched: "
+ numMatches);
}
// Invoke the pattern matching
// method on each file.
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) {
find(file);
return CONTINUE;
}
// Invoke the pattern matching
// method on each directory.
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) {
find(dir);
return CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file,
IOException exc) {
System.err.println(exc);
return CONTINUE;
}
}
static void usage() {
System.err.println("java Find <path>" +
" -name \"<glob_pattern>\"");
System.exit(-1);
}
public static void main(String[] args)
throws IOException {
if (args.length < 3 || !args[1].equals("-name"))
usage();
Path startingDir = Paths.get(args[0]);
String pattern = args[2];
Finder finder = new Finder(pattern);
Files.walkFileTree(startingDir, finder);
finder.done();
}
}