# 目录树生成脚本 — 技术方案设计(Windows 平台) > 任务 ID: T023 | 项目 ID: PROJ-20260509011 > 功能清单: F013-F020(8 个功能,全部审批通过) > 运行平台: Windows > 设计日期: 2026-05-16 > 设计者: 脚本架构师 --- ## 1. 技术选型评估 ### 1.1 功能级可行性分析(BAT vs Python) | 功能编号 | 功能名称 | BAT 可行性 | Python 可行性 | 关键问题 | |---------|---------|-----------|--------------|---------| | F013 | 接收路径输入 | ✅ 完全可行 | ✅ 完全可行 | BAT: `%~1` 即可;Python: `argparse` | | F014 | 加载忽略配置 | ⚠️ 勉强可行 | ✅ 完全可行 | BAT 读取配置文件需逐行 `for /f`,无结构化解析能力 | | F015 | 递归遍历目录 | ⚠️ 勉强可行 | ✅ 完全可行 | BAT `for /r` 可递归但无法灵活控制跳过逻辑 | | F016 | 生成目录树(├── 字符) | ❌ **不可行** | ✅ 完全可行 | Windows CMD 默认代码页 936 (GBK) 无法正确显示 `├` `─` 等 Unicode 制表符;需 `chcp 65001` 且仍有渲染问题。BAT 字符串拼接缩进极其困难 | | F017 | 生成文件树 | ⚠️ 勉强可行 | ✅ 完全可行 | BAT `dir /s/b` 可列出文件,但格式化输出困难 | | F018 | 终端输出 | ⚠️ 有条件可行 | ✅ 完全可行 | BAT 需处理 `chcp 65001` 编码切换,输出重定向时易乱码 | | F019 | Markdown 保存 | ⚠️ 勉强可行 | ✅ 完全可行 | BAT 可 `echo > file.md`,但 Markdown 结构化内容拼接繁琐 | | F020 | 统计信息(目录数/文件数/总大小) | ❌ **困难** | ✅ 完全可行 | BAT 无原生文件大小累加能力,`%%~zI` 在 `for` 循环中可用但累加大文件时易溢出(BAT 整数仅支持 32 位) | ### 1.2 BAT 核心缺陷总结 1. **Unicode 制表符渲染**:F016 要求输出 `├──` `└──` `│` 等字符,Windows CMD 默认代码页下这些字符会显示为乱码。虽然 `chcp 65001` 可以切换 UTF-8,但在 Windows 10 之前的系统上存在严重兼容性问题,且输出重定向到文件时编码转换不可靠。 2. **字符串处理**:BAT 的字符串拼接、缩进管理极其笨拙,无法优雅实现树形缩进逻辑。 3. **大文件统计**:BAT 的整数运算限制在 32 位有符号范围(约 ±21 亿),总大小超过 2GB 时会溢出。 4. **错误处理**:BAT 缺乏结构化异常处理机制(try/catch),错误恢复困难。 5. **配置解析**:BAT 无法优雅解析结构化配置文件(JSON/INI),只能逐行文本处理。 ### 1.3 评估结论 **BAT 无法胜任 F016(目录树 Unicode 渲染)和 F020(大文件统计),其余功能也均存在明显缺陷。** --- ## 2. 技术选型结论 ### 最终选择:Python 3.8+(标准库,无第三方依赖) **理由:** | 维度 | 说明 | |-----|------| | 功能覆盖 | 8 个功能全部可完美实现,无妥协 | | Unicode 支持 | Python 原生 UTF-8 支持,`├──` 等字符渲染无问题 | | 标准库 | `pathlib`、`os`、`argparse`、`datetime` 覆盖所有需求 | | 文件大小 | Python 整数自动扩展,无溢出问题 | | 跨版本兼容 | Python 3.8+ 覆盖 Windows 7 SP1 及以上所有版本 | | 可维护性 | 代码结构清晰,模块化设计,易于扩展 | | 部署 | 单文件 `.py`,用户只需安装 Python(Windows 10/11 可通过 Microsoft Store 一键安装) | --- ## 3. 项目结构 ``` tree_generator/ ├── tree_gen.py # 主程序(单文件,包含所有模块) ├── .treeignore # 忽略配置文件(可选,放在目标目录下) ├── tree_output.md # 默认输出文件(运行后生成) └── README.md # 使用文档(由 Web 文档生成 Agent 创建) ``` **设计原则:** - 主程序 `tree_gen.py` 为单文件,不拆分为多模块,便于分发和使用 - 忽略配置文件 `.treeignore` 放在目标目录下,遵循 `.gitignore` 惯例 - 输出文件默认在当前工作目录生成 --- ## 4. 模块说明表 | 模块名 | 负责功能 | 输入 | 输出 | 依赖 | |-------|---------|------|------|------| | `ArgParser` | F013 路径输入 | 命令行参数 `sys.argv` | 解析后的配置对象(目标路径、输出路径、深度限制等) | `argparse` 标准库 | | `IgnoreLoader` | F014 忽略配置 | 配置文件路径(`.treeignore`) | 忽略目录名称集合 `set[str]` | `pathlib` 标准库 | | `DirectoryScanner` | F015 递归遍历 | 目标路径 + 忽略集合 | 目录树结构(嵌套字典) | `pathlib`、`os` 标准库 | | `TreeFormatter` | F016 目录树 + F017 文件树 | 目录树结构 | 格式化字符串(含 `├──` 缩进) | 无额外依赖 | | `TerminalOutput` | F018 终端输出 | 格式化字符串 | 终端显示(stdout) | `sys` 标准库(编码设置) | | `MarkdownWriter` | F019 Markdown 保存 | 格式化字符串 + 输出路径 | `.md` 文件 | `pathlib` 标准库 | | `StatisticsCollector` | F020 统计信息 | 目录树结构 | 统计信息字典(目录数、文件数、总大小) | `os` 标准库 | --- ## 5. 技术选型结论总结 ``` ┌─────────────────────────────────────────────────────┐ │ 最终技术选型:Python 3.8+ 标准库 │ │ │ │ 核心文件:tree_gen.py(单文件,约 300-400 行) │ │ 配置文件:.treeignore(可选) │ │ 输出文件:tree_output.md(默认) │ │ │ │ 选择理由: │ │ 1. BAT 无法正确处理 Unicode 制表符(F016) │ │ 2. BAT 整数溢出问题(F020 大文件统计) │ │ 3. Python 标准库完全覆盖所有 8 个功能 │ │ 4. 单文件部署,零第三方依赖 │ └─────────────────────────────────────────────────────┘ ``` --- ## 6. 错误处理策略 ### 6.1 异常分类与处理 | 异常类型 | 触发条件 | 处理策略 | 用户提示 | |---------|---------|---------|---------| | `PathNotFoundError` | 目标路径不存在 | 捕获 `FileNotFoundError`,退出并提示 | `错误: 路径 "xxx" 不存在,请检查输入` | | `PermissionError` | 无权限访问目录 | 跳过该目录,记录警告,继续遍历 | `警告: 无权限访问 "xxx",已跳过` | | `SymlinkLoopError` | 符号链接循环 | 使用 `pathlib.Path.resolve()` 检测,跳过已访问路径 | `警告: 检测到符号链接循环,已跳过` | | `EncodingError` | 文件名含特殊字符 | 使用 `errors='replace'` 容错 | 静默替换,不中断 | | `OutputWriteError` | 无法写入输出文件 | 捕获 `IOError`/`PermissionError`,回退到仅终端输出 | `警告: 无法写入文件,仅显示到终端` | | `InvalidConfigError` | 忽略配置文件格式错误 | 使用默认忽略列表,记录警告 | `警告: 配置文件格式错误,使用默认配置` | ### 6.2 退出码规范 | 退出码 | 含义 | |-------|------| | 0 | 成功完成 | | 1 | 参数错误(路径不存在、格式错误) | | 2 | 运行时错误(写入失败等) | | 3 | 中断(Ctrl+C) | --- ## 7. 接口定义 ### 7.1 数据流图 ``` ┌──────────┐ ┌──────────────┐ ┌─────────────────┐ │ 命令行参数 │────▶│ ArgParser │────▶│ Config 对象 │ │ sys.argv │ │ (F013) │ │ {path, output, │ └──────────┘ └──────────────┘ │ ignore, depth} │ └────────┬────────┘ │ ┌───────────────────────────┼───────────────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │IgnoreLoader │ │Directory │ │Statistics │ │(F014) │ │Scanner │ │Collector │ │ │ │(F015) │ │(F020) │ │.treeignore │ │ │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ ▼ ▼ │ ignore_set tree_dict │ (set[str]) (嵌套字典) │ │ │ ▼ │ ┌──────────────────┐ │ │ TreeFormatter │◀────────────────────┘ │ (F016/F017) │ │ │ │ tree_str │ │ file_list_str │ └────────┬────────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Terminal │ │ Markdown │ │ 统计信息 │ │ Output │ │ Writer │ │ 输出 │ │ (F018) │ │ (F019) │ │ │ │ │ │ │ │ │ │ stdout │ │ tree_output │ │ 目录/文件/ │ │ │ │ .md │ │ 总大小 │ └──────────────┘ └──────────────┘ └──────────────┘ ``` ### 7.2 核心数据结构 ```python # Config 对象(ArgParser 输出) Config = { "target_path": Path, # 目标目录路径 "output_path": Path, # Markdown 输出路径(默认 tree_output.md) "ignore_dirs": set[str], # 忽略目录名称集合 "max_depth": int | None, # 最大递归深度(None = 无限制) "files_only": bool, # 仅显示文件模式 "dirs_only": bool, # 仅显示目录模式 } # 目录树结构(DirectoryScanner 输出) TreeDict = { "name": str, # 目录/文件名 "path": Path, # 完整路径 "is_dir": bool, # 是否为目录 "children": list[TreeDict], # 子节点列表(目录时有效) "size": int, # 文件大小(文件时有效) } # 统计信息(StatisticsCollector 输出) Statistics = { "dir_count": int, # 目录数量 "file_count": int, # 文件数量 "total_size": int, # 总大小(字节) "total_size_human": str, # 人类可读大小(如 "1.23 GB") } ``` ### 7.3 模块间接口 | 调用方 | 被调用方 | 接口方法 | 参数 | 返回值 | |-------|---------|---------|------|--------| | `main()` | `ArgParser` | `parse_args()` | `sys.argv[1:]` | `Config` | | `main()` | `IgnoreLoader` | `load_ignores(config)` | `Config` | `set[str]` | | `main()` | `DirectoryScanner` | `scan(path, ignore_set, max_depth)` | `Path, set[str], int\|None` | `TreeDict` | | `main()` | `TreeFormatter` | `format_tree(tree, style)` | `TreeDict, str` | `str` | | `main()` | `TreeFormatter` | `format_files(tree)` | `TreeDict` | `str` | | `main()` | `TerminalOutput` | `output(text)` | `str` | `None` | | `main()` | `MarkdownWriter` | `write(text, path)` | `str, Path` | `None` | | `main()` | `StatisticsCollector` | `collect(tree)` | `TreeDict` | `Statistics` | --- ## 8. 忽略配置方案(F014) ### 8.1 配置文件格式 文件名:`.treeignore`(放在目标目录下) ``` # 忽略配置示例 # 每行一个目录名,支持 # 注释 .git .gitignore node_modules __pycache__ *.pyc .DS_Store Thumbs.db venv .env .idea .vscode dist build *.egg-info ``` ### 8.2 内置默认忽略列表 当 `.treeignore` 不存在时,使用以下默认列表: ```python DEFAULT_IGNORE = { ".git", ".svn", ".hg", # 版本控制 "node_modules", "bower_components", # Node.js "__pycache__", "*.pyc", ".pytest_cache", # Python ".idea", ".vscode", # IDE "dist", "build", "target", # 构建产物 ".DS_Store", "Thumbs.db", # 系统文件 "venv", ".venv", "env", # 虚拟环境 } ``` ### 8.3 匹配规则 - 目录名精确匹配(不区分大小写,Windows 特性) - 支持 `*` 通配符(如 `*.pyc`) - 忽略配置仅作用于目录级别,不递归检查文件内容 --- ## 9. 树形输出格式规范(F016/F017) ### 9.1 目录树格式 ``` 项目根目录/ ├── src/ │ ├── main.py │ ├── utils/ │ │ ├── helper.py │ │ └── config.py │ └── models/ │ └── user.py ├── tests/ │ ├── test_main.py │ └── test_utils.py ├── config.json └── README.md ``` **规则:** - 目录名后加 `/` 后缀 - 分支符:`├── `(有后续兄弟节点)/ `└── `(最后一个节点) - 缩进线:`│ `(有后续兄弟节点)/ ` `(无后续兄弟节点) - 缩进单位:4 个字符(`│` + 3 空格 或 4 空格) ### 9.2 文件树格式 ``` 文件列表: C:\project\src\main.py C:\project\src\utils\helper.py C:\project\src\utils\config.py C:\project\src\models\user.py C:\project\tests\test_main.py C:\project\tests\test_utils.py C:\project\config.json C:\project\README.md ``` **规则:** - 每个文件一行,带完整绝对路径 - 使用 Windows 风格路径分隔符 `\` - 按字母顺序排序 ### 9.3 统计信息格式 ``` 统计信息: 目录数: 5 文件数: 8 总大小: 24.5 KB (25,088 字节) ``` --- ## 10. Markdown 保存方案(F019) ### 10.1 输出文件格式 ```markdown # 目录树 — 项目根目录 > 生成时间: 2026-05-16 16:00:00 > 目标路径: C:\project > 扫描深度: 无限制 ## 目录结构 ``` 项目根目录/ ├── src/ │ ├── main.py │ └── utils/ │ └── helper.py ├── config.json └── README.md ``` ## 文件列表 | # | 文件路径 | |---|---------| | 1 | `C:\project\src\main.py` | | 2 | `C:\project\src\utils\helper.py` | | 3 | `C:\project\config.json` | | 4 | `C:\project\README.md` | ## 统计信息 - **目录数:** 3 - **文件数:** 4 - **总大小:** 1.2 KB (1,234 字节) ``` ### 10.2 保存策略 - 默认文件名:`tree_output.md`(当前工作目录) - 可通过 `-o` / `--output` 参数指定自定义路径 - 文件已存在时覆盖写入(不追加) - 编码:UTF-8 with BOM(Windows 记事本兼容) --- ## 11. 命令行接口设计 ``` usage: tree_gen.py [-h] [-o OUTPUT] [-d DEPTH] [-f] [-D] [-i IGNORE_FILE] [path] 目录树生成脚本 - Windows 平台 positional arguments: path 目标目录路径(默认: 当前目录) options: -h, --help 显示帮助信息 -o OUTPUT, --output OUTPUT Markdown 输出文件路径(默认: tree_output.md) -d DEPTH, --depth DEPTH 最大递归深度(默认: 无限制) -f, --files-only 仅显示文件树 -D, --dirs-only 仅显示目录树 -i IGNORE_FILE, --ignore IGNORE_FILE 忽略配置文件路径(默认: 目标目录下的 .treeignore) ``` ### 使用示例 ```bash # 扫描当前目录 python tree_gen.py # 扫描指定目录 python tree_gen.py C:\Users\test\project # 指定输出文件和深度 python tree_gen.py C:\project -o output.md -d 3 # 仅显示文件 python tree_gen.py C:\project -f # 自定义忽略配置 python tree_gen.py C:\project -i my_ignore.txt ``` --- ## 12. 编码规范 | 项目 | 规范 | |-----|------| | 文件编码 | UTF-8(Python 源文件) | | 输出编码 | UTF-8 with BOM(Markdown 文件) | | 终端编码 | UTF-8(通过 `sys.stdout.reconfigure(encoding='utf-8')` 设置) | | 行尾符 | LF(Python 标准) | | 缩进 | 4 空格 | | Python 版本 | 3.8+ | --- ## 13. 后续任务 | 任务 | 负责 Agent | 状态 | |-----|-----------|------| | T021: Python 代码实现 | BAT 编码 Agent(实际应为 Python 编码 Agent) | 待激活 | | T022: 功能测试验证 | 测试验证 Agent | 待激活 | | T024: 使用文档编写 | Web 文档生成 Agent | 待激活 | --- *文档结束。等待审批后进入编码阶段。*