前言

一直觉得windows下的tree命令生成的目录树方便又美观,无奈微软并不会提供tree命令的实现源码,于是就拿Python撸了一个出来。

效果请看下图吧,代码也在下文中。如果你想了解实现细节,我在下文中也有一些简单的解释。

效果

下图是一个Intellij IDEA工程的目录树(不同字体可能显示略有差异):

├─.idea
│  ├─inspectionProfiles
│  ├─misc.xml
│  ├─modules.xml
│  └─workspace.xml
├─out
│  └─production
│     ├─TempJava
│     │  ├─A.class
│     │  ├─B.class
│     │  ├─Main$HeapSort.class
│     │  ├─Main$LexicalOrder.class
│     │  ├─Main$ListNode.class
│     │  ├─Main$TreeNode.class
│     │  ├─Main.class
│     │  ├─Solution.class
│     │  ├─Temp.class
│     │  └─Test.class
│     └─.DS_Store
├─src
│  ├─Main.java
│  ├─Solution.java
│  ├─Temp.java
│  └─Test.java
├─.DS_Store
└─TempJava.iml

代码

import os
import os.path

BRANCH = '├─'
LAST_BRANCH = '└─'
TAB = '│  '
EMPTY_TAB = '   '


def get_dir_list(path, placeholder=''):
    folder_list = [folder for folder in os.listdir(path) if os.path.isdir(os.path.join(path, folder))]
    file_list = [file for file in os.listdir(path) if os.path.isfile(os.path.join(path, file))]
    result = ''
    for folder in folder_list[:-1]:
        result += placeholder + BRANCH + folder + '\n'
        result += get_dir_list(os.path.join(path, folder), placeholder + TAB)
    if folder_list:
        result += placeholder + (BRANCH if file_list else LAST_BRANCH) + folder_list[-1] + '\n'
        result += get_dir_list(os.path.join(path, folder_list[-1]), placeholder + (TAB if file_list else EMPTY_TAB))
    for file in file_list[:-1]:
        result += placeholder + BRANCH + file + '\n'
    if file_list:
        result += placeholder + LAST_BRANCH + file_list[-1] + '\n'
    return result


if __name__ == '__main__':
    print(get_dir_list('./TempJava'))

get_dir_list()的参数为需要生成目录树的根目录的路径,其返回值是一个字符串,你可以直接打印出来,或保存到文件中。

实现细节

目录树的生成是一个递归调用,get_dir_list()每次调用都是在遍历当前文件夹下的子文件夹和文件,当遇到子文件夹时就递归调用get_dir_list(),然后将其返回结果加入到当前结果中。

为了实现不同目录层级缩进的效果,我们需要在对应行前加入一些“占位符”。再仔细想想可以发现对于同一层级,占位符都是一致的,所以我们可以把占位符当作递归的参数,每次递归都增加一些参数。

看上图可以发现,每一次增加的占位符基本上都是'│ ';但也存在例外情况,如上图中out文件夹下,遍历production文件夹时,其最后一个占位符就是' ',原因是什么呢?因为out文件夹下仅有production文件夹,导致其不必继续输出其他分支(和其他的文件夹对比看看)。所以在遍历到最后一个子文件夹时,需要判断是否存在子文件,如存在,则使用'│ ',否则使用' '

还有一个需要注意的地方是,对于最后一个子文件夹/文件,其应以'└─'开头,而非'├─'。这个其实很容易实现,看代码就明白啦,主要就是list[:-1]这个的应用。

大概细节就是这样,如果你有更好的实现,还请不吝赐教~