383. Java IO API - Java 文件查找工具:Find 示例完整解析
這是一個用 Java 編寫的簡單文件查找工具,它模仿了 Linux 中的 find 命令,允許你通過 glob 模式查找符合特定命名規(guī)則的文件或目錄。
/**
* Sample code that finds files that match the specified glob pattern.
* For more information on what constitutes a glob pattern, see
* https://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob
*
* The file or directories that match the pattern are printed to
* standard out. The number of matches is also printed.
*
* When executing this application, you must put the glob pattern
* in quotes, so the shell will not expand any wild cards:
* java Find . -name "*.java"
*/
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();
}
}
?? 功能簡介
這個程序會:
- 遍歷指定目錄及其子目錄;
- 使用 glob 模式匹配文件或目錄名;
- 輸出所有匹配項的路徑;
- 輸出匹配總數(shù)。
例如:
$ java Find . -name "*.java"
會打印當前目錄及其子目錄中所有以 .java 結尾的文件。
? 示例代碼結構詳解
1?? main() 方法:入口程序
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();
}
- 接收命令行參數(shù):路徑 +
-name+ glob 模式。 - 使用
Files.walkFileTree()遍歷文件樹。 - 調(diào)用 Finder 匹配并輸出結果。
2?? Finder 類:自定義文件訪問器
public static class Finder extends SimpleFileVisitor<Path> {
繼承 SimpleFileVisitor<Path>,重寫幾個核心方法來自定義行為:
?? 構造方法:初始化 glob 匹配器
matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern);
示例:"*.java" ? 匹配所有 Java 源文件。
?? find() 方法:核心匹配邏輯
void find(Path file) {
Path name = file.getFileName();
if (name != null && matcher.matches(name)) {
numMatches++;
System.out.println(file);
}
}
- 使用
matcher.matches(...)判斷文件是否匹配; - 統(tǒng)計匹配個數(shù);
- 輸出匹配路徑。
?? preVisitDirectory():遍歷目錄前的匹配邏輯
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
find(dir); // 匹配目錄名
return CONTINUE;
}
不僅可以匹配文件,也能匹配目錄名稱。
?? visitFile():遍歷文件時的匹配邏輯
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
find(file);
return CONTINUE;
}
對每一個文件調(diào)用 find() 方法。
?? visitFileFailed():錯誤處理
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.println(exc);
return CONTINUE;
}
如果文件訪問失敗(如權限問題),打印錯誤但不終止遍歷。
?? done():打印統(tǒng)計結果
void done() {
System.out.println("Matched: " + numMatches);
}
在遍歷完成后顯示總共找到多少個匹配項。
?? 運行示例
$ java Find . -name "*.html"
輸出:
./index.html
./docs/help.html
Matched: 2
?? 擴展思路與練習建議
| 擴展功能 | 實現(xiàn)方法 |
|---|---|
| 僅查找文件,不查找目錄 | 在 visitFile() 中匹配,preVisitDirectory() 不調(diào)用 find()
|
過濾某些目錄(如 .git) |
在 preVisitDirectory() 中判斷名稱并返回 SKIP_SUBTREE
|
| 使用正則表達式匹配 | 替換為 getPathMatcher("regex:" + pattern)
|
| 跟蹤符號鏈接 | 使用 EnumSet.of(FileVisitOption.FOLLOW_LINKS) 作為 walkFileTree() 參數(shù) |
| 查找結果寫入文件 | 將 System.out.println(...) 替換為寫入 BufferedWriter
|
?? 小貼士:PathMatcher 注意事項
-
僅文件名匹配:要記得用
file.getFileName()而不是整個Path,否則路徑中目錄部分也會被匹配。 -
通配符需要加引號:如
"*.java",否則 shell 會提前展開通配符。
?? 錯誤處理建議
- 可以改進
visitFileFailed()的行為,比如記錄失敗日志或重試。 - 使用
try-catch包裝walkFileTree(),處理整體異常。
? 結語
這個 Find 示例是理解 Java 文件遍歷與模式匹配機制的絕佳入門材料。它涵蓋了:
-
walkFileTree的用法; - 文件訪問器的重寫方式;
-
PathMatcher的實際應用; - glob 模式的基礎。
在實際項目中,結合該示例可構建自己的文件過濾工具、批量處理腳本或配置管理工具。