设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间01 现在时间01 缓存数据 当你走完一段之后回头看,你会发现,那些真正能被记得的事真的是没有多少,真正无法忘记的人屈指可数,真正有趣的日子不过是那么一些,而真正需要害怕的也是寥寥无几。

当你走完一段之后回头看,你会发现,那些真正能被记得的事真的是没有多少,真正无法忘记的人屈指可数,真正有趣的日子不过是那么一些,而真正需要害怕的也是寥寥无几。

查看: 90|回复: 0

基于Go语言实现一个目录树打印工具

[复制链接]

  离线 

TA的专栏

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 打卡月天数:0
  • 打卡总奖励:39
  • 最近打卡:2024-03-26 19:57:23
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
41
主题
31
精华
0
金钱
159
积分
82
注册时间
2023-9-29
最后登录
2025-9-11

发表于 2025-9-8 08:59:48 | 显示全部楼层 |阅读模式
在日常开发中,我们经常需要可视化项目的目录结构。无论是编写文档、分享项目结构,还是单纯了解代码布局,一个清晰的目录树展示都至关重要。今天我将介绍一款用Go语言开发的目录树打印工具,它不仅能生成美观的目录结构图,还提供多种实用功能!

功能亮点

多层级展示:支持自定义深度限制
隐藏文件处理:可选显示隐藏文件/文件夹
多种输出方式:控制台打印、保存文件、复制到剪贴板
美观可视化:使用emoji图标标识不同类型
灵活连接线:可选的树形连接线展示
智能排序:目录优先,按名称排序

技术实现解析


核心数据结构
  1. type DirectoryPrinter struct {
  2.     rootDir       string      // 根目录路径
  3.     showHidden    bool        // 是否显示隐藏文件
  4.     currentDepth  int         // 当前递归深度
  5.     maxDepth      int         // 最大深度限制
  6.     indentSymbol  string      // 缩进符号(4个空格)
  7.     folderSymbol  string      // 文件夹图标
  8.     fileSymbol    string      // 文件图标
  9.     output        []string    // 输出内容收集
  10.     showConnector bool        // 是否显示连接线
  11. }
复制代码
关键算法逻辑

1.文件排序策略:
目录优先于文件
相同类型按名称升序排列
  1. sort.Slice(filteredEntries, func(i, j int) bool {
  2.     if filteredEntries[i].IsDir() != filteredEntries[j].IsDir() {
  3.         return filteredEntries[i].IsDir()
  4.     }
  5.     return filteredEntries[i].Name() < filteredEntries[j].Name()
  6. })
复制代码
2.连接线生成逻辑:
非最后一项:├──
最后一项:└──
  1. if dp.showConnector {
  2.     isLast := index == total-1
  3.     connector := "├── "
  4.     if isLast {
  5.         connector = "└── "
  6.     }
  7.     return fmt.Sprintf("%s%s📁 %s\n", prefix, connector, entry.Name())
  8. }
复制代码
3.递归深度控制:
  1. if dp.maxDepth > 0 && dp.currentDepth >= dp.maxDepth {
  2.     return nil
  3. }
复制代码
使用指南


命令行参数

参数说明示例--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
使用示例

基本用法(显示当前目录结构):
  1. directory_printer
复制代码
显示隐藏文件并限制深度:
  1. directory_printer --show-hidden --max-depth 2
复制代码
保存到文件并显示连接线:
  1. directory_printer --output-file project_tree.txt --show-connector
复制代码
输出示例
  1. 📂 my-project    📁 src        📁 controllers        📁 models        📁 views    📁 config    📁 public        📁 css        📁 js        📁 images    📄 README.md    📄 .gitignore    📄 go.mod
复制代码
带连接线版本:
  1. 📂 my-project├── 📁 src│   ├── 📁 controllers│   ├── 📁 models│   └── 📁 views├── 📁 config├── 📁 public│   ├── 📁 css│   ├── 📁 js│   └── 📁 images├── 📄 README.md├── 📄 .gitignore└── 📄 go.mod
复制代码
实现细节解析


根目录处理
  1. rootName := filepath.Base(rootDir)
  2. if rootName == "." {
  3.     // 获取当前工作目录的绝对路径
  4.     absPath, err := filepath.Abs(rootDir)
  5.     if err != nil {
  6.         rootName = "current_directory"
  7.     } else {
  8.         rootName = filepath.Base(absPath)
  9.     }
  10. }
  11. printer.output = append(printer.output, fmt.Sprintf("📂 %s\n", rootName))
复制代码
输出处理逻辑
  1. // 保存到文件
  2. if *outputFile != "" {
  3.     err = saveToFile(*outputFile, printer.output)
  4. }

  5. // 复制到剪贴板
  6. if *copyClipBoard {
  7.     content := strings.Join(printer.output, "")
  8.     err = clipboard.WriteAll(content)
  9. }

  10. ​​​​​​​// 控制台输出
  11. fmt.Println("\n=== Directory Structure ===")
  12. for _, line := range printer.output {
  13.     fmt.Print(line)
  14. }
复制代码
递归目录遍历
  1. childPrinter := &DirectoryPrinter{
  2.     rootDir:       filepath.Join(dp.rootDir, entry.Name()),
  3.     showHidden:    dp.showHidden,
  4.     currentDepth:  dp.currentDepth + 1,
  5.     maxDepth:      dp.maxDepth,
  6.     indentSymbol:  dp.indentSymbol,
  7.     folderSymbol:  dp.folderSymbol,
  8.     fileSymbol:    dp.fileSymbol,
  9.     output:        dp.output,
  10.     showConnector: dp.showConnector,
  11. }
  12. if err := childPrinter.printDirectory(); err != nil {
  13.     return err
  14. }
  15. dp.output = childPrinter.output
复制代码
附录

完整代码
  1. package main

  2. import (
  3.         "flag"
  4.         "fmt"
  5.         "os"
  6.         "path/filepath"
  7.         "sort"
  8.         "strings"

  9.         "github.com/atotto/clipboard"
  10. )

  11. type DirectoryPrinter struct {
  12.         rootDir       string
  13.         showHidden    bool
  14.         currentDepth  int
  15.         maxDepth      int
  16.         indentSymbol  string
  17.         folderSymbol  string
  18.         fileSymbol    string
  19.         output        []string
  20.         showConnector bool // 新增字段控制是否显示连接线
  21. }

  22. func (dp *DirectoryPrinter) printDirectory() error {
  23.         // 检查是否超过最大深度
  24.         if dp.maxDepth > 0 && dp.currentDepth >= dp.maxDepth {
  25.                 return nil
  26.         }

  27.         entries, err := os.ReadDir(dp.rootDir)
  28.         if err != nil {
  29.                 return err
  30.         }

  31.         // 过滤隐藏文件
  32.         var filteredEntries []os.DirEntry
  33.         for _, entry := range entries {
  34.                 if dp.showHidden || !strings.HasPrefix(entry.Name(), ".") {
  35.                         filteredEntries = append(filteredEntries, entry)
  36.                 }
  37.         }

  38.         // 按类型(目录优先)和名称排序
  39.         sort.Slice(filteredEntries, func(i, j int) bool {
  40.                 // 首先按类型排序(目录在前)
  41.                 if filteredEntries[i].IsDir() != filteredEntries[j].IsDir() {
  42.                         return filteredEntries[i].IsDir()
  43.                 }
  44.                 // 同类型按名称排序
  45.                 return filteredEntries[i].Name() < filteredEntries[j].Name()
  46.         })

  47.         total := len(filteredEntries)
  48.         for i, entry := range filteredEntries {
  49.                 prefix := strings.Repeat(dp.indentSymbol, dp.currentDepth)
  50.                 var line string
  51.                 if entry.IsDir() {
  52.                         line = dp.buildFolderLine(prefix, i, total, entry)
  53.                 } else {
  54.                         line = dp.buildFileLine(prefix, i, total, entry)
  55.                 }
  56.                 dp.output = append(dp.output, line)

  57.                 // 递归处理子文件夹
  58.                 if entry.IsDir() {
  59.                         childPrinter := &DirectoryPrinter{
  60.                                 rootDir:       filepath.Join(dp.rootDir, entry.Name()),
  61.                                 showHidden:    dp.showHidden,
  62.                                 currentDepth:  dp.currentDepth + 1,
  63.                                 maxDepth:      dp.maxDepth,
  64.                                 indentSymbol:  dp.indentSymbol,
  65.                                 folderSymbol:  dp.folderSymbol,
  66.                                 fileSymbol:    dp.fileSymbol,
  67.                                 output:        dp.output,
  68.                                 showConnector: dp.showConnector,
  69.                         }
  70.                         if err := childPrinter.printDirectory(); err != nil {
  71.                                 return err
  72.                         }
  73.                         dp.output = childPrinter.output
  74.                 }
  75.         }

  76.         return nil
  77. }

  78. func (dp *DirectoryPrinter) buildFolderLine(prefix string, index, total int, entry os.DirEntry) string {
  79.         if dp.showConnector {
  80.                 isLast := index == total-1
  81.                 connector := "├── "
  82.                 if isLast {
  83.                         connector = "└── "
  84.                 }
  85.                 return fmt.Sprintf("%s%s📁 %s\n", prefix, connector, entry.Name())
  86.         }
  87.         return fmt.Sprintf("%s📁 %s\n", prefix, entry.Name())
  88. }

  89. func (dp *DirectoryPrinter) buildFileLine(prefix string, index, total int, entry os.DirEntry) string {
  90.         if dp.showConnector {
  91.                 isLast := index == total-1
  92.                 connector := "├── "
  93.                 if isLast {
  94.                         connector = "└── "
  95.                 }
  96.                 return fmt.Sprintf("%s%s📄 %s\n", prefix, connector, entry.Name())
  97.         }
  98.         return fmt.Sprintf("%s📄 %s\n", prefix, entry.Name())
  99. }

  100. func usage() {
  101.         fmt.Println("Usage: directory_printer [OPTIONS] [PATH]")
  102.         fmt.Println("\nOptions:")
  103.         fmt.Println("  --show-hidden               Include hidden files and directories")
  104.         fmt.Println("  --output-file <file_path>   Save the directory structure to a file")
  105.         fmt.Println("  --copy-ClipBoard            Copy Directory structure to clipboard")
  106.         fmt.Println("  --max-depth <number>        Maximum directory depth to display (0 for all levels, 1 for root only)")
  107.         fmt.Println("  --show-connector            Show connector characters (├── and └──)")
  108.         fmt.Println("  --help                      Display this help message")
  109.         fmt.Println("\nExample:")
  110.         fmt.Println("  directory_printer --show-hidden --max-depth 2 --output-file output.txt /path/to/directory")
  111. }

  112. func isDirectory(path string) bool {
  113.         fileInfo, err := os.Stat(path)
  114.         if err != nil {
  115.                 return false
  116.         }
  117.         return fileInfo.IsDir()
  118. }

  119. func saveToFile(filePath string, content []string) error {
  120.         file, err := os.Create(filePath)
  121.         if err != nil {
  122.                 return err
  123.         }
  124.         defer file.Close()

  125.         for _, line := range content {
  126.                 if _, err := file.WriteString(line); err != nil {
  127.                         return err
  128.                 }
  129.         }

  130.         return nil
  131. }

  132. func main() {
  133.         showHidden := flag.Bool("show-hidden", false, "Include hidden files and directories")
  134.         outputFile := flag.String("output-file", "", "Save the directory structure to a file")
  135.         copyClipBoard := flag.Bool("copy-ClipBoard", true, "Copy Directory structure to clipboard")
  136.         maxDepth := flag.Int("max-depth", 0, "Maximum directory depth to display (0 for all levels, 1 for root only)")
  137.         showConnector := flag.Bool("show-connector", false, "Show connector characters (├── and └──)")
  138.         help := flag.Bool("help", false, "Display this help message")
  139.         flag.Parse()

  140.         if *help {
  141.                 usage()
  142.                 os.Exit(0)
  143.         }

  144.         var rootDir string
  145.         if len(flag.Args()) == 0 {
  146.                 rootDir = "."
  147.         } else {
  148.                 rootDir = flag.Arg(0)
  149.         }

  150.         if !isDirectory(rootDir) {
  151.                 fmt.Printf("Error: %s is not a valid directory\n", rootDir)
  152.                 os.Exit(1)
  153.         }

  154.         printer := &DirectoryPrinter{
  155.                 rootDir:       rootDir,
  156.                 showHidden:    *showHidden,
  157.                 currentDepth:  0,
  158.                 maxDepth:      *maxDepth,
  159.                 indentSymbol:  "    ", // 使用4个空格作为视觉缩进
  160.                 folderSymbol:  "",
  161.                 fileSymbol:    "",
  162.                 output:        []string{},
  163.                 showConnector: *showConnector,
  164.         }

  165.         rootName := filepath.Base(rootDir)
  166.         if rootName == "." {
  167.                 // 获取当前工作目录的绝对路径
  168.                 absPath, err := filepath.Abs(rootDir)
  169.                 if err != nil {
  170.                         rootName = "current_directory"
  171.                 } else {
  172.                         rootName = filepath.Base(absPath)
  173.                 }
  174.         }
  175.         printer.output = append(printer.output, fmt.Sprintf("📂 %s\n", rootName))
  176.         // 增加根目录的缩进
  177.         printer.currentDepth = 1

  178.         err := printer.printDirectory()
  179.         if err != nil {
  180.                 fmt.Printf("Error: %v\n", err)
  181.                 os.Exit(1)
  182.         }

  183.         if *outputFile != "" {
  184.                 err = saveToFile(*outputFile, printer.output)
  185.                 if err != nil {
  186.                         fmt.Printf("Failed to save to file: %v\n", err)
  187.                         os.Exit(1)
  188.                 }
  189.                 fmt.Printf("Directory structure saved to: %s\n", *outputFile)
  190.         }
  191.         if *copyClipBoard {
  192.                 content := strings.Join(printer.output, "")
  193.                 err = clipboard.WriteAll(content)
  194.                 if err != nil {
  195.                         fmt.Printf("Failed to copy to clipboard: %v\n", err)
  196.                 } else {
  197.                         fmt.Println("Directory structure copied to clipboard")
  198.                 }
  199.         }

  200.         fmt.Println("\n=== Directory Structure ===")
  201.         for _, line := range printer.output {
  202.                 fmt.Print(line)
  203.         }
  204. }
复制代码
总结

这款Go语言实现的目录树打印工具通过简洁的代码实现了强大的功能,无论是开发者快速查看项目结构,还是编写技术文档时展示目录布局,它都是一个得力的助手。清晰的emoji标识、灵活的输出选项和可定制的显示深度,让它成为你开发工具箱中不可或缺的一员。
到此这篇关于基于Go语言实现一个目录树打印工具的文章就介绍到这了,更多相关Go目录树打印内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表