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