python模块代码
import os
import json
import xml.etree.ElementTree as ET
from typing import List, Optional, Dict, Union
from pathlib import Path
class DirectoryTreeExporter:
def __init__(self,
root_path: str,
output_file: str,
fmt: str = 'txt',
show_root: bool = True,
include_hidden_files: bool = False,
include_hidden_dirs: bool = False,
folders_only: bool = False,
allowed_extensions: Optional[List[str]] = None):
"""
参数说明:
- root_path: 要扫描的根目录路径
- output_file: 输出文件路径
- fmt: 输出格式(txt/json/xml)
- show_root: 是否显示根目录为树的顶层节点
- include_hidden_files: 是否包含隐藏文件
- include_hidden_dirs: 是否包含隐藏文件夹
- folders_only: 是否仅显示文件夹
- allowed_extensions: 允许的文件扩展名列表(例如 ['.py', '.txt'])
"""
self.root = Path(root_path).resolve()
self.output_file = output_file
self.fmt = fmt.lower()
self.show_root = show_root
self.include_hidden_files = include_hidden_files
self.include_hidden_dirs = include_hidden_dirs
self.folders_only = folders_only
self.allowed_extensions = set(allowed_extensions or [])
def _is_hidden(self, path: Path) -> bool:
"""跨平台的隐藏文件/目录检测"""
if os.name == 'nt':
hidden_attr = os.stat(path).st_file_attributes
return hidden_attr & 2
else:
return path.name.startswith('.')
def _should_include(self,
path: Path,
is_dir: bool) -> bool:
"""过滤条件判断"""
if is_dir:
if not self.include_hidden_dirs and self._is_hidden(path):
return False
else:
if not self.include_hidden_files and self._is_hidden(path):
return False
if self.folders_only and not is_dir:
return False
if not is_dir and self.allowed_extensions:
return path.suffix.lower() in self.allowed_extensions
return True
def _build_tree(self,
node: Path,
tree_dict: Dict) -> None:
"""递归构建树形结构"""
for child in node.iterdir():
if not self._should_include(child, child.is_dir()):
continue
entry = {
'name': child.name,
'type': 'directory' if child.is_dir() else 'file',
'children': []
}
if child.is_dir():
self._build_tree(child, entry)
tree_dict['children'].append(entry)
def generate(self) -> None:
"""生成并导出目录树"""
tree = {
'name': self.root.name,
'type': 'directory',
'children': []
} if self.show_root else {'children': []}
if self.show_root:
self._build_tree(self.root, tree)
else:
for child in self.root.iterdir():
if self._should_include(child, child.is_dir()):
entry = {
'name': child.name,
'type': 'directory' if child.is_dir() else 'file',
'children': []
}
if child.is_dir():
self._build_tree(child, entry)
tree['children'].append(entry)
output_methods = {
'txt': self._generate_txt,
'json': self._generate_json,
'xml': self._generate_xml
}
if self.fmt not in output_methods:
raise ValueError(f"不支持的格式: {self.fmt}")
output_methods[self.fmt](tree)
def _generate_txt(self, tree: Dict) -> None:
"""生成文本树形结构"""
def _recurse(node: Dict, depth: int, lines: List[str], is_last: bool) -> None:
prefix = ""
if depth > 0:
prefix = "│ " * (depth - 1) + ("└── " if is_last else "├── ")
lines.append(f"{prefix}{node['name']} ({node['type']})")
children = node.get('children', [])
for i, child in enumerate(children):
_recurse(child, depth + 1, lines, i == len(children)-1)
lines = []
if self.show_root:
_recurse(tree, 0, lines, True)
else:
for child in tree['children']:
_recurse(child, 0, lines, False)
lines = self.replace_vertical_below_lconnector(lines)
with open(self.output_file, 'w', encoding='utf-8') as f:
f.write("\n".join(lines))
def _generate_json(self, tree: Dict) -> None:
"""生成JSON结构"""
with open(self.output_file, 'w', encoding='utf-8') as f:
json.dump(tree, f, indent=2, ensure_ascii=False)
def _generate_xml(self, tree: Dict) -> None:
"""生成XML结构"""
def _add_node(parent: ET.Element, node: Dict) -> None:
elem = ET.SubElement(parent, node['type'])
elem.set('name', node['name'])
for child in node.get('children', []):
_add_node(elem, child)
root = ET.Element('directory_tree')
if self.show_root:
_add_node(root, tree)
else:
for child in tree['children']:
_add_node(root, child)
tree = ET.ElementTree(root)
tree.write(self.output_file, encoding='utf-8', xml_declaration=True)
if __name__ == "__main__":
exporter = DirectoryTreeExporter(
root_path=r"D:\Creation\JiLeiFunc\root_dir",
output_file=r"D:\Creation\JiLeiFunc\tree.txt",
fmt="txt",
show_root=True,
include_hidden_files=False,
folders_only=False
)
exporter.generate()