首页 > 解决方案 > 如何使用 Files.newDirectoryStream 和递归以特定方式打印目录树?

问题描述

例如,我有一个文件夹“大学”。我需要按如下所示的方式打印“大学”内的所有文件夹和文件:

        university
        ├── file.txt
        ├── floor 1
        │   └── room 102
        │       └── file.txt
        ├── floor 2
        │   ├── left wing
        │   │   ├── kitchen
        │   │   │   └── kitchen.txt
        │   │   └── room 232
        │   └── right wing
        │       └── room 252
        │           ├── file1.txt
        │           └── file2.txt
        └── floor 3
            └── room 374
                └── file.txt

我可以使用递归和 Files.newDirectoryStream() 方法,或 Files.list() 作为模拟。我知道我的问题的一些解决方案,比如二叉树或类似的东西,但我只能使用我所说的。

这是我尝试过的:

public class Main {
private static final String UNIVERSITY = "university";
private static final Path UNIVERSITY_FOLDER = Paths.get(UNIVERSITY);
private static final StringBuffer result = new StringBuffer();

public static void main(String[] args) throws IOException {
    createTree(UNIVERSITY_FOLDER);

}

private static void printTree(Path path) throws IOException {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
        Iterator<Path> iter = stream.iterator();
        while (iter.hasNext()) {
            Path currentFile = iter.next();
            appendSpaces(currentFile);
            if (iter.hasNext()) {
                result.append("├── ");
            } else {
                result.append("└── ");
            }
            result.append(currentFile.getFileName());
            result.append(System.lineSeparator());
            if (Files.isDirectory(currentFile)) {
                printTree(currentFile);
            }
        }
    } catch (DirectoryIteratorException ex) {
        throw ex.getCause();
    }

}

private static void createTree(Path path) throws IOException {
    result.append(path.getFileName());
    result.append(System.lineSeparator());
    printTree(path);
    System.out.println(result.toString());
}

private static void appendSpaces(Path path) {
    for (int i = 0; i < (path.getNameCount() - 2); i++) {
        result.append("│   ");
    }
}

}

我的代码正在运行,但它会打印出类似的内容:

university
├── file.txt
├── floor 1
│   └── room 102
│   │   └── file.txt
├── floor 2
│   ├── left wing
│   │   ├── kitchen
│   │   │   └── kitchen.txt
│   │   └── room 232
│   └── right wing
│   │   └── room 252
│   │   │   ├── file1.txt
│   │   │   └── file2.txt
└── floor 3
│   └── room 374
│   │   └── file.txt

我需要一个额外的条件来打印这个符号“│”,但也许我走错了路。我将不胜感激。

标签: java

解决方案


正如您在房间 252 中看到的元素,当您 时printSpaces,您需要打印 3 个“缩进”,并且您的代码始终打印"| "为缩进。但是它需要为 252 打印的是"| "第一个缩进,但" "作为第二个和第三个缩进。

但是再想象一下 3 楼有一个 375 房间,那么对于 374 房间的条目,您必须打印" "第一个缩进和"| " 第二个缩进。

这表明打印缩进的代码(当前传递一个列出要打印的缩进数量的单个数字)从根本上被破坏了。它需要传递一个数组或其他类似列表的结构。你不通过'3',而是通过'true,false,false'或类似这些线的东西。

更糟糕的是,您当前将文件名传递给 appendSpaces 并编写一些代码对其进行路径深度分析,以了解甚至有多少缩进,这只是一个非首发:给定的路径只是不知道,每个缩进,无论您需要画线还是只需要一个空白块。

有很多解决方案,但最常用的一种是使用递归助手。对于大多数递归算法,您需要一些变量来跟踪诸如“多少缩进”之类的信息(或者在这种情况下,一堆标志,每个缩进一个,以指示是否绘制条形图)。对于第一次调用(您的代码的一个用户进行),这些跟踪器变量没有任何意义,因此您需要创建一个帮助程序。因此,而不是:

public static void printTree(... params ...) {
   // code that will eventaully call printTree recursively
}

你这样做:

public static void printTree(.... params ...) {
    printTree0(param1, param2, param3, new boolean[0]);
}

private static void printTree0(... params ..., boolean[] indents) {
    // code that calls printTree0 recursively
}

这是一个非常常见的模式:

  • 公共方法只包含一个调用私有方法的单行代码,以相同的顺序传递所有参数,此外还有一些变量,全部为初始值(0,或者在这种情况下,是一个空的布尔数组,表示根级别不需要缩进)。

  • 私有方法(附加 a0是一种常见的 java 习惯用法,表示“这是一个辅助方法”)是递归编写的,公共方法不是。

您的indents跟踪器变量包含这些标志,是否打印条形图。

每次递归时,您都会创建一个新的布尔数组,并添加正确的缩进:

boolean[] newIndents = Arrays.copyOf(indents, indents.length + 1);
newIndents[indents.length] = true or false (depending on whether you want bars or not).

从那里我相信你可以弄清楚,因为你几乎完成了任务。


推荐阅读