Java檔案樹


FileVisitor API可以遞回地處理檔案樹中的所有檔案和目錄。當要對檔案樹中的所有或某些檔案或目錄執行某些操作時,FileVisitor API非常有用。

SimpleFileVisitor類是FileVisitor介面的基本實現。當存取檔案/目錄時,SimpleFileVisitor類不執行任何操作。可以從SimpleFileVisitor類繼承檔案存取-FileVisitor 類,並且只覆蓋需要的方法。

FileVisitor介面的方法

編號 含義
1 FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)在存取目錄中的條目之前呼叫一次。
2 FileVisitResult postVisitDirectory(T dir,IOException exc)已存取目錄中的後呼叫項。如果在目錄的疊代期間丟擲了任何異常,則將異常物件作為第二個引數傳遞給此方法。如果此方法的第二個引數為null,則在目錄疊代期間沒有異常。
3 FileVisitResult visitFile(T file, BasicFileAttributes attrs) 是在當存取目錄中的檔案時呼叫。
4 FileVisitResult visitFileFailed(T file, IOException exc)是當由於任何原因而無法存取檔案或目錄時呼叫。

下表列出了FileVisitResult的列舉常數及其說明 -

列舉常數 描述/含義
CONTINUE 繼續處理
SKIP_SIBLINGS 繼續處理而不存取檔案或目錄的同級。
SKIP_SUBTREE 繼續處理,而不存取目錄中的條目。
TERMINATE 終止檔案存取過程。

不需要在檔案存取類的所有四個方法中編寫邏輯。要複製目錄,請使用preVisitDirectory()方法來建立一個新目錄,並使用visitFile()方法來複製該檔案。

以下程式碼顯示如何列印目錄的子目錄和檔案的名稱。

import static java.nio.file.FileVisitResult.CONTINUE;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Main {
    public static void main(String[] args) {
        Path startDir = Paths.get("");
        FileVisitor<Path> visitor = getFileVisitor();
        try {
            Files.walkFileTree(startDir, visitor);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static FileVisitor<Path> getFileVisitor() {
        class DirVisitor<Path> extends SimpleFileVisitor<Path> {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {

                System.out.format("%s [Directory]%n", dir);
                return CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                System.out.format("%s [File,  Size: %s  bytes]%n", file, attrs.size());
                return CONTINUE;
            }
        }
        FileVisitor<Path> visitor = new DirVisitor<>();
        return visitor;
    }
}

上面的程式碼生成以下結果。

 [Directory]
.classpath [File,  Size: 232  bytes]
.project [File,  Size: 382  bytes]
.settings [Directory]
.settings\org.eclipse.core.resources.prefs [File,  Size: 57  bytes]
bin [Directory]
bin\Main$1DirVisitor.class [File,  Size: 1648  bytes]
bin\Main.class [File,  Size: 1338  bytes]
destfile.txt [File,  Size: 25  bytes]
luci3.txt [File,  Size: 25  bytes]
my_second_file.txt [File,  Size: 0  bytes]
person.ser [File,  Size: 160  bytes]
personext.ser [File,  Size: 93  bytes]
primitives.dat [File,  Size: 42  bytes]
randomaccessfile.txt [File,  Size: 18  bytes]
src [Directory]
src\Calculator.java [File,  Size: 0  bytes]
src\Main.class [File,  Size: 1111  bytes]
src\Main.java [File,  Size: 1172  bytes]
stdout.txt [File,  Size: 34  bytes]
test.txt [File,  Size: 13  bytes]
ziptest.zip [File,  Size: 22  bytes]

範例

以下程式碼顯示如何使用FileVisitor API刪除目錄樹,把目錄 C:\Java_Dev 和其中的所有內容刪除。

import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.FileVisitResult.TERMINATE;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Main {
    public static void main(String[] args) {
        Path dirToDelete = Paths.get("C:\\Java_Dev");
        FileVisitor<Path> visitor = getFileVisitor();

        try {
            Files.walkFileTree(dirToDelete, visitor);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    public static FileVisitor<Path> getFileVisitor() {

        class DeleteDirVisitor extends SimpleFileVisitor<Path> {
            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
                FileVisitResult result = CONTINUE;
                if (e != null) {
                    System.out.format("Error deleting  %s.  %s%n", dir, e.getMessage());
                    result = TERMINATE;
                } else {
                    Files.delete(dir);
                    System.out.format("Deleted directory  %s%n", dir);
                }
                return result;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                Files.delete(file);
                System.out.format("Deleted file %s%n", file);
                return CONTINUE;
            }
        }
        FileVisitor<Path> visitor = new DeleteDirVisitor();
        return visitor;
    }
}

上面的程式碼生成以下結果。

Deleted file C:\Java_Dev\dir1\test1.txt
Deleted directory  C:\Java_Dev\dir1
Deleted directory  C:\Java_Dev\dir1 - 副本
Deleted file C:\Java_Dev\dir2\test1.txt
Deleted directory  C:\Java_Dev\dir2
Deleted file C:\Java_Dev\test1.txt
Deleted file C:\Java_Dev\twinkle.txt
Deleted directory  C:\Java_Dev

範例-2

以下程式碼顯示如何使用walkFileTree()方法跟隨符號連結。

import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.Set;
import static  java.nio.file.FileVisitOption.FOLLOW_LINKS;
public class Main {
  public static void main(String[] args) throws Exception {
    Path startDir = Paths.get("");
    FileVisitor<Path> visitor = create your visitor;

    Set<FileVisitOption> options = EnumSet.of(FOLLOW_LINKS);

    int depth = Integer.MAX_VALUE;

    Files.walkFileTree(startDir, options, depth, visitor);
  }
}

模式匹配

可以使用glob和正規表示式模式對字串形式的Path物件執行模式匹配。

功能介面PathMatcher用於執行匹配。它包含一個matches(Path path)方法,如果指定的路徑匹配模式,則該方法返回true
模式字串由兩部分組成,語法和模式由冒號分隔:

syntax:pattern

語法的值是globregex。模式部分遵循取決於語法部分的值。glob模式使用以下語法規則:

  • * 匹配零個或多個字元,而不會交叉目錄邊界。
  • ** 匹配零個或多個字元跨目錄邊界。
  • ? 只匹配一個字元。
  • \ 跳脫以下字元的特殊含義。
  • \\ 匹配單個反斜槓
  • \* 匹配一個星號。

放在括號[]中的字元稱為括號表示式,它匹配單個字元。如:[aeiou]將匹配aeiou

兩個字元之間的破折號指定範圍。[a-z]匹配az之間的所有字母。左括號後的感嘆號()被視為否定。 [!abc]匹配除了abc之外的所有字元。

通過在大括號({})中指定逗號分隔的子模式來使用一組子模式。 例如,{txt,java,doc}匹配txtjavadoc

路徑的根元件的匹配是實現相關的。以下程式碼顯示了如何使用PathMatcher物件將路徑與glob模式匹配。

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;

public class Main {
    public static void main(String[] args) {
        String globPattern = "glob:**txt";
        PathMatcher matcher = FileSystems.getDefault().getPathMatcher(globPattern);
        Path path = Paths.get("C:\\Java_Dev\\test1.txt");
        boolean matched = matcher.matches(path);
        System.out.format("%s matches  %s:  %b%n", globPattern, path, matched);
    }
}

執行上面的程式碼,得到以下結果 -

glob:**txt matches  C:\Java_Dev\test1.txt:  true