
离线 TA的专栏
- 打卡等级:初来乍到
- 打卡总天数:3
- 打卡月天数:0
- 打卡总奖励:39
- 最近打卡:2024-03-26 19:57:23
|
在日常开发中,我们经常需要可视化项目的目录结构。无论是编写文档、分享项目结构,还是单纯了解代码布局,一个清晰的目录树展示都至关重要。今天我将介绍一款用Go语言开发的目录树打印工具,它不仅能生成美观的目录结构图,还提供多种实用功能!
功能亮点
多层级展示:支持自定义深度限制
隐藏文件处理:可选显示隐藏文件/文件夹
多种输出方式:控制台打印、保存文件、复制到剪贴板
美观可视化:使用emoji图标标识不同类型
灵活连接线:可选的树形连接线展示
智能排序:目录优先,按名称排序
技术实现解析
核心数据结构
- type DirectoryPrinter struct {
- rootDir string // 根目录路径
- showHidden bool // 是否显示隐藏文件
- currentDepth int // 当前递归深度
- maxDepth int // 最大深度限制
- indentSymbol string // 缩进符号(4个空格)
- folderSymbol string // 文件夹图标
- fileSymbol string // 文件图标
- output []string // 输出内容收集
- showConnector bool // 是否显示连接线
- }
复制代码 关键算法逻辑
1.文件排序策略:
目录优先于文件
相同类型按名称升序排列 - sort.Slice(filteredEntries, func(i, j int) bool {
- if filteredEntries[i].IsDir() != filteredEntries[j].IsDir() {
- return filteredEntries[i].IsDir()
- }
- return filteredEntries[i].Name() < filteredEntries[j].Name()
- })
复制代码2.连接线生成逻辑:
非最后一项:├──
最后一项:└── - if dp.showConnector {
- isLast := index == total-1
- connector := "├── "
- if isLast {
- connector = "└── "
- }
- return fmt.Sprintf("%s%s📁 %s\n", prefix, connector, entry.Name())
- }
复制代码3.递归深度控制: - if dp.maxDepth > 0 && dp.currentDepth >= dp.maxDepth {
- return nil
- }
复制代码 使用指南
命令行参数
参数说明示例--show-hidden显示隐藏文件--show-hidden--output-file保存到文件--output-file tree.txt--copy-ClipBoard复制到剪贴板--copy-ClipBoard--max-depth设置最大深度--max-depth 3--show-connector显示连接线--show-connector--help显示帮助--help
使用示例
基本用法(显示当前目录结构): 显示隐藏文件并限制深度: - directory_printer --show-hidden --max-depth 2
复制代码保存到文件并显示连接线: - directory_printer --output-file project_tree.txt --show-connector
复制代码 输出示例
- 📂 my-project 📁 src 📁 controllers 📁 models 📁 views 📁 config 📁 public 📁 css 📁 js 📁 images 📄 README.md 📄 .gitignore 📄 go.mod
复制代码带连接线版本: - 📂 my-project├── 📁 src│ ├── 📁 controllers│ ├── 📁 models│ └── 📁 views├── 📁 config├── 📁 public│ ├── 📁 css│ ├── 📁 js│ └── 📁 images├── 📄 README.md├── 📄 .gitignore└── 📄 go.mod
复制代码 实现细节解析
根目录处理
- rootName := filepath.Base(rootDir)
- if rootName == "." {
- // 获取当前工作目录的绝对路径
- absPath, err := filepath.Abs(rootDir)
- if err != nil {
- rootName = "current_directory"
- } else {
- rootName = filepath.Base(absPath)
- }
- }
- printer.output = append(printer.output, fmt.Sprintf("📂 %s\n", rootName))
复制代码 输出处理逻辑
- // 保存到文件
- if *outputFile != "" {
- err = saveToFile(*outputFile, printer.output)
- }
- // 复制到剪贴板
- if *copyClipBoard {
- content := strings.Join(printer.output, "")
- err = clipboard.WriteAll(content)
- }
- // 控制台输出
- fmt.Println("\n=== Directory Structure ===")
- for _, line := range printer.output {
- fmt.Print(line)
- }
复制代码 递归目录遍历
- childPrinter := &DirectoryPrinter{
- rootDir: filepath.Join(dp.rootDir, entry.Name()),
- showHidden: dp.showHidden,
- currentDepth: dp.currentDepth + 1,
- maxDepth: dp.maxDepth,
- indentSymbol: dp.indentSymbol,
- folderSymbol: dp.folderSymbol,
- fileSymbol: dp.fileSymbol,
- output: dp.output,
- showConnector: dp.showConnector,
- }
- if err := childPrinter.printDirectory(); err != nil {
- return err
- }
- dp.output = childPrinter.output
复制代码 附录
完整代码 - package main
- import (
- "flag"
- "fmt"
- "os"
- "path/filepath"
- "sort"
- "strings"
- "github.com/atotto/clipboard"
- )
- type DirectoryPrinter struct {
- rootDir string
- showHidden bool
- currentDepth int
- maxDepth int
- indentSymbol string
- folderSymbol string
- fileSymbol string
- output []string
- showConnector bool // 新增字段控制是否显示连接线
- }
- func (dp *DirectoryPrinter) printDirectory() error {
- // 检查是否超过最大深度
- if dp.maxDepth > 0 && dp.currentDepth >= dp.maxDepth {
- return nil
- }
- entries, err := os.ReadDir(dp.rootDir)
- if err != nil {
- return err
- }
- // 过滤隐藏文件
- var filteredEntries []os.DirEntry
- for _, entry := range entries {
- if dp.showHidden || !strings.HasPrefix(entry.Name(), ".") {
- filteredEntries = append(filteredEntries, entry)
- }
- }
- // 按类型(目录优先)和名称排序
- sort.Slice(filteredEntries, func(i, j int) bool {
- // 首先按类型排序(目录在前)
- if filteredEntries[i].IsDir() != filteredEntries[j].IsDir() {
- return filteredEntries[i].IsDir()
- }
- // 同类型按名称排序
- return filteredEntries[i].Name() < filteredEntries[j].Name()
- })
- total := len(filteredEntries)
- for i, entry := range filteredEntries {
- prefix := strings.Repeat(dp.indentSymbol, dp.currentDepth)
- var line string
- if entry.IsDir() {
- line = dp.buildFolderLine(prefix, i, total, entry)
- } else {
- line = dp.buildFileLine(prefix, i, total, entry)
- }
- dp.output = append(dp.output, line)
- // 递归处理子文件夹
- if entry.IsDir() {
- childPrinter := &DirectoryPrinter{
- rootDir: filepath.Join(dp.rootDir, entry.Name()),
- showHidden: dp.showHidden,
- currentDepth: dp.currentDepth + 1,
- maxDepth: dp.maxDepth,
- indentSymbol: dp.indentSymbol,
- folderSymbol: dp.folderSymbol,
- fileSymbol: dp.fileSymbol,
- output: dp.output,
- showConnector: dp.showConnector,
- }
- if err := childPrinter.printDirectory(); err != nil {
- return err
- }
- dp.output = childPrinter.output
- }
- }
- return nil
- }
- func (dp *DirectoryPrinter) buildFolderLine(prefix string, index, total int, entry os.DirEntry) string {
- if dp.showConnector {
- isLast := index == total-1
- connector := "├── "
- if isLast {
- connector = "└── "
- }
- return fmt.Sprintf("%s%s📁 %s\n", prefix, connector, entry.Name())
- }
- return fmt.Sprintf("%s📁 %s\n", prefix, entry.Name())
- }
- func (dp *DirectoryPrinter) buildFileLine(prefix string, index, total int, entry os.DirEntry) string {
- if dp.showConnector {
- isLast := index == total-1
- connector := "├── "
- if isLast {
- connector = "└── "
- }
- return fmt.Sprintf("%s%s📄 %s\n", prefix, connector, entry.Name())
- }
- return fmt.Sprintf("%s📄 %s\n", prefix, entry.Name())
- }
- func usage() {
- fmt.Println("Usage: directory_printer [OPTIONS] [PATH]")
- fmt.Println("\nOptions:")
- fmt.Println(" --show-hidden Include hidden files and directories")
- fmt.Println(" --output-file <file_path> Save the directory structure to a file")
- fmt.Println(" --copy-ClipBoard Copy Directory structure to clipboard")
- fmt.Println(" --max-depth <number> Maximum directory depth to display (0 for all levels, 1 for root only)")
- fmt.Println(" --show-connector Show connector characters (├── and └──)")
- fmt.Println(" --help Display this help message")
- fmt.Println("\nExample:")
- fmt.Println(" directory_printer --show-hidden --max-depth 2 --output-file output.txt /path/to/directory")
- }
- func isDirectory(path string) bool {
- fileInfo, err := os.Stat(path)
- if err != nil {
- return false
- }
- return fileInfo.IsDir()
- }
- func saveToFile(filePath string, content []string) error {
- file, err := os.Create(filePath)
- if err != nil {
- return err
- }
- defer file.Close()
- for _, line := range content {
- if _, err := file.WriteString(line); err != nil {
- return err
- }
- }
- return nil
- }
- func main() {
- showHidden := flag.Bool("show-hidden", false, "Include hidden files and directories")
- outputFile := flag.String("output-file", "", "Save the directory structure to a file")
- copyClipBoard := flag.Bool("copy-ClipBoard", true, "Copy Directory structure to clipboard")
- maxDepth := flag.Int("max-depth", 0, "Maximum directory depth to display (0 for all levels, 1 for root only)")
- showConnector := flag.Bool("show-connector", false, "Show connector characters (├── and └──)")
- help := flag.Bool("help", false, "Display this help message")
- flag.Parse()
- if *help {
- usage()
- os.Exit(0)
- }
- var rootDir string
- if len(flag.Args()) == 0 {
- rootDir = "."
- } else {
- rootDir = flag.Arg(0)
- }
- if !isDirectory(rootDir) {
- fmt.Printf("Error: %s is not a valid directory\n", rootDir)
- os.Exit(1)
- }
- printer := &DirectoryPrinter{
- rootDir: rootDir,
- showHidden: *showHidden,
- currentDepth: 0,
- maxDepth: *maxDepth,
- indentSymbol: " ", // 使用4个空格作为视觉缩进
- folderSymbol: "",
- fileSymbol: "",
- output: []string{},
- showConnector: *showConnector,
- }
- rootName := filepath.Base(rootDir)
- if rootName == "." {
- // 获取当前工作目录的绝对路径
- absPath, err := filepath.Abs(rootDir)
- if err != nil {
- rootName = "current_directory"
- } else {
- rootName = filepath.Base(absPath)
- }
- }
- printer.output = append(printer.output, fmt.Sprintf("📂 %s\n", rootName))
- // 增加根目录的缩进
- printer.currentDepth = 1
- err := printer.printDirectory()
- if err != nil {
- fmt.Printf("Error: %v\n", err)
- os.Exit(1)
- }
- if *outputFile != "" {
- err = saveToFile(*outputFile, printer.output)
- if err != nil {
- fmt.Printf("Failed to save to file: %v\n", err)
- os.Exit(1)
- }
- fmt.Printf("Directory structure saved to: %s\n", *outputFile)
- }
- if *copyClipBoard {
- content := strings.Join(printer.output, "")
- err = clipboard.WriteAll(content)
- if err != nil {
- fmt.Printf("Failed to copy to clipboard: %v\n", err)
- } else {
- fmt.Println("Directory structure copied to clipboard")
- }
- }
- fmt.Println("\n=== Directory Structure ===")
- for _, line := range printer.output {
- fmt.Print(line)
- }
- }
复制代码 总结
这款Go语言实现的目录树打印工具通过简洁的代码实现了强大的功能,无论是开发者快速查看项目结构,还是编写技术文档时展示目录布局,它都是一个得力的助手。清晰的emoji标识、灵活的输出选项和可定制的显示深度,让它成为你开发工具箱中不可或缺的一员。
到此这篇关于基于Go语言实现一个目录树打印工具的文章就介绍到这了,更多相关Go目录树打印内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:  进行删除处理。
4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
|