#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 项目代码导出工具 用于将整个电商项目的代码导出到文本文件中 """ import os import datetime from pathlib import Path class CodeExporter: def __init__(self, project_root=None): """ 初始化代码导出器 :param project_root: 项目根目录,默认为当前目录 """ self.project_root = Path(project_root) if project_root else Path('.') self.output_file = None # 需要导出的文件扩展名 self.include_extensions = { '.py', '.html', '.css', '.js', '.sql', '.txt', '.md', '.yml', '.yaml', '.json', '.xml', '.ini', '.cfg' } # 需要排除的目录 self.exclude_dirs = { 'venv', '.venv', 'env', '.env', '__pycache__', '.git', '.idea', '.vscode', 'node_modules', 'logs', 'temp', 'tmp', '.pytest_cache', '.coverage', 'htmlcov', 'dist', 'build' } # 需要排除的文件 self.exclude_files = { '.DS_Store', 'Thumbs.db', '.gitignore', '*.pyc', '*.pyo', '*.log', '*.tmp', '*.bak', '*.swp', '*.swo' } # 特殊处理的文件(即使没有扩展名也要包含) self.special_files = { 'Dockerfile', 'requirements.txt', 'README', 'LICENSE', 'Makefile', 'Procfile', '.dockerignore' } def should_include_file(self, file_path): """ 判断文件是否应该被包含在导出中 :param file_path: 文件路径 :return: bool """ file_name = file_path.name file_suffix = file_path.suffix.lower() # 检查特殊文件 if file_name in self.special_files: return True # 检查扩展名 if file_suffix in self.include_extensions: return True return False def should_exclude_dir(self, dir_path): """ 判断目录是否应该被排除 :param dir_path: 目录路径 :return: bool """ dir_name = dir_path.name return dir_name in self.exclude_dirs or dir_name.startswith('.') def get_file_info(self, file_path): """ 获取文件信息 :param file_path: 文件路径 :return: dict """ try: stat = file_path.stat() return { 'size': stat.st_size, 'modified': datetime.datetime.fromtimestamp(stat.st_mtime), 'relative_path': file_path.relative_to(self.project_root) } except Exception as e: return { 'size': 0, 'modified': datetime.datetime.now(), 'relative_path': file_path.relative_to(self.project_root), 'error': str(e) } def read_file_content(self, file_path): """ 读取文件内容 :param file_path: 文件路径 :return: str """ try: # 尝试用UTF-8编码读取 with open(file_path, 'r', encoding='utf-8') as f: return f.read() except UnicodeDecodeError: try: # 如果UTF-8失败,尝试GBK编码 with open(file_path, 'r', encoding='gbk') as f: return f.read() except UnicodeDecodeError: try: # 如果还是失败,尝试latin-1编码 with open(file_path, 'r', encoding='latin-1') as f: return f.read() except Exception as e: return f"[无法读取文件内容: {str(e)}]" except Exception as e: return f"[读取文件时发生错误: {str(e)}]" def scan_project(self): """ 扫描项目目录,获取所有需要导出的文件 :return: list """ files_to_export = [] for root, dirs, files in os.walk(self.project_root): root_path = Path(root) # 过滤掉需要排除的目录 dirs[:] = [d for d in dirs if not self.should_exclude_dir(root_path / d)] for file in files: file_path = root_path / file if self.should_include_file(file_path): file_info = self.get_file_info(file_path) files_to_export.append({ 'path': file_path, 'info': file_info }) # 按相对路径排序 files_to_export.sort(key=lambda x: str(x['info']['relative_path'])) return files_to_export def export_to_file(self, output_filename=None): """ 导出代码到文件 :param output_filename: 输出文件名 """ if not output_filename: timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") output_filename = f"project_code_export_{timestamp}.txt" self.output_file = output_filename files_to_export = self.scan_project() print(f"开始导出项目代码...") print(f"项目根目录: {self.project_root.absolute()}") print(f"找到 {len(files_to_export)} 个文件需要导出") print(f"输出文件: {output_filename}") with open(output_filename, 'w', encoding='utf-8') as output: # 写入文件头 self.write_header(output, files_to_export) # 写入每个文件的内容 for i, file_data in enumerate(files_to_export, 1): file_path = file_data['path'] file_info = file_data['info'] print(f"正在处理 ({i}/{len(files_to_export)}): {file_info['relative_path']}") self.write_file_section(output, file_path, file_info) # 写入文件尾 self.write_footer(output) print(f"\n✅ 导出完成!") print(f"输出文件: {output_filename}") print(f"文件大小: {os.path.getsize(output_filename) / 1024:.2f} KB") def write_header(self, output, files_to_export): """ 写入文件头部信息 """ output.write("=" * 80 + "\n") output.write("项目代码导出文件\n") output.write("=" * 80 + "\n") output.write(f"项目名称: 基于Python的线上电商系统\n") output.write(f"导出时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") output.write(f"项目路径: {self.project_root.absolute()}\n") output.write(f"文件总数: {len(files_to_export)}\n") output.write("=" * 80 + "\n\n") # 写入文件目录 output.write("📁 文件目录:\n") output.write("-" * 50 + "\n") for file_data in files_to_export: file_info = file_data['info'] size_kb = file_info['size'] / 1024 if file_info['size'] > 0 else 0 output.write(f"{file_info['relative_path']} ({size_kb:.1f} KB)\n") output.write("\n" + "=" * 80 + "\n\n") def write_file_section(self, output, file_path, file_info): """ 写入单个文件的内容 """ relative_path = file_info['relative_path'] # 文件分隔符 output.write("🔸" + "=" * 78 + "\n") output.write(f"📄 文件: {relative_path}\n") output.write(f"📊 大小: {file_info['size']} bytes ({file_info['size'] / 1024:.2f} KB)\n") output.write(f"🕒 修改时间: {file_info['modified'].strftime('%Y-%m-%d %H:%M:%S')}\n") if 'error' in file_info: output.write(f"⚠️ 错误: {file_info['error']}\n") output.write("🔸" + "=" * 78 + "\n\n") # 文件内容 content = self.read_file_content(file_path) output.write(content) # 确保文件结尾有换行 if not content.endswith('\n'): output.write('\n') output.write("\n\n") def write_footer(self, output): """ 写入文件尾部信息 """ output.write("=" * 80 + "\n") output.write("导出完成\n") output.write(f"导出时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") output.write("=" * 80 + "\n") def export_summary(self): """ 导出项目摘要信息 """ files_to_export = self.scan_project() # 按文件类型统计 type_stats = {} total_size = 0 for file_data in files_to_export: file_path = file_data['path'] file_info = file_data['info'] ext = file_path.suffix.lower() or '无扩展名' if ext not in type_stats: type_stats[ext] = {'count': 0, 'size': 0} type_stats[ext]['count'] += 1 type_stats[ext]['size'] += file_info['size'] total_size += file_info['size'] print("\n📊 项目统计信息:") print("-" * 50) print(f"总文件数: {len(files_to_export)}") print(f"总大小: {total_size / 1024:.2f} KB") print("\n📋 文件类型统计:") for ext, stats in sorted(type_stats.items(), key=lambda x: x[1]['count'], reverse=True): print(f"{ext:>10}: {stats['count']:>3} 个文件, {stats['size'] / 1024:>6.1f} KB") def main(): """ 主函数 """ print("🚀 项目代码导出工具") print("=" * 50) # 创建导出器 exporter = CodeExporter() # 显示项目摘要 exporter.export_summary() # 询问是否继续导出 print("\n" + "=" * 50) choice = input("是否继续导出完整代码到文件? (y/n): ").lower().strip() if choice in ['y', 'yes', '是']: # 询问输出文件名 output_name = input("请输入输出文件名 (直接回车使用默认名称): ").strip() if not output_name: output_name = None # 开始导出 exporter.export_to_file(output_name) else: print("取消导出。") if __name__ == "__main__": main()