Go模块管理踩坑实录:从入门到放弃再到真香

大家好,我是老墨。 很早之前,翻译了一篇 Golang 官方的模块管理文档中文,那篇有点“官方”了,今天老墨搞一篇接地气的模块实战教程。 说实话,我第一次接触 Go 模块的时候,内心是拒绝的。 GOPATH 那套虽然老,但好歹能用。突然冒出来个 go.mod、go.sum,还有什么 replace、indirect,看着就头大。更别提那些看起来像乱码的版本号:v1.2.3-0.20210101000000-abcdef123456,这是什么鬼? 但折腾了几个月后,我发现:真香。 今天就把我踩过的坑、学到的经验,全部掏出来。不讲理论,只讲实战。 为什么要用Go模块? 先说说为什么要用模块,而不是继续用GOPATH。 GOPATH 的三大痛点 版本管理是灾难 所有依赖都在 $GOPATH/src 下 同一个包只能有一个版本 项目 A 用 v1.0,项目 B 用 v2.0?抱歉,做不到 依赖地狱 依赖的依赖的依赖……谁也不知道用了什么版本 换台机器编译?祝你好运 团队协作?每个人的依赖都不一样 无法复现构建 今天能编译,明天就不行了 因为依赖包更新了 你根本不知道 Go 模块解决了什么 每个项目独立管理依赖 版本号明确,可复现构建 依赖关系清晰,记录在 go.mod 校验和保证安全性(go.sum) 支持私有仓库和代理 快速上手:5分钟创建第一个模块 1. 初始化模块 1# 创建项目目录 2mkdir myproject 3cd myproject 4 5# 初始化模块 6go mod init github.com/yourusername/myproject 这会生成一个go.mod文件: 1module github.com/yourusername/myproject 2 3go 1.21 就这么简单。 2. 添加依赖 写点代码,导入个包: ...

2026-02-25 · 4 min · 751 words · 老墨

[GoLang避坑实战-18] 实战 CLI 工具:30 分钟用 urfave/cli 构建你的极客武器

大家好,我是极客老墨。 上一篇我们写了个小工具——一个简单的命令行计算器,需要从命令行读参数, 我们直接通过 os.Args 来解析命令行参数,这种方式太底层、太原始了,非常容易出错。 说实话,Go 虽然内置了 flag 包,但用起来仍然不是很方便。想要做个像样的 CLI 工具,光靠 flag 远远不够。 今天我们就来聊聊如何用 Go 社区最受欢迎的 urfave/cli 框架,打造真正的极客工具。 为什么不用 flag 包? Go 标准库的 flag 包确实能解析参数,但问题是: 1// 用 flag 包写个简单的工具 2var name = flag.String("name", "", "your name") 3var age = flag.Int("age", 0, "your age") 4 5flag.Parse() 6 7if *name == "" { 8 fmt.Println("name is required") 9 os.Exit(1) 10} 看到没?每个参数都要手动验证,帮助信息要自己拼,子命令?不存在的,得自己实现。 写个小工具还行,但要做个像 git、docker 那样的专业 CLI,光靠 flag 真的会疯。 ...

2025-11-20 · 7 min · 1448 words · 老墨

Python教程30:综合项目-任务管理器

Python教程30:综合项目-任务管理器 “纸上得来终觉浅,绝知此事要躬行。” 经过第三部分13课的学习,我们掌握了函数、模块、文件操作、异常处理、测试等核心知识。今天我们通过一个完整项目——命令行任务管理器,把这些知识综合应用起来。 1. 项目需求 功能需求 开发一个命令行任务管理器(Todo List),支持: 基本功能 添加任务 查看任务列表 标记任务完成 删除任务 进阶功能 任务优先级(高、中、低) 任务分类(工作、生活、学习) 截止日期 数据持久化(保存到文件) 额外功能 搜索任务 统计信息 导入/导出 技术要求 使用函数和类组织代码 模块化设计 完善的异常处理 文件操作(JSON格式存储) 单元测试 命令行交互 2. 项目结构 1task_manager/ 2├── task_manager/ # 主包 3│ ├── __init__.py 4│ ├── task.py # 任务类 5│ ├── manager.py # 管理器类 6│ ├── storage.py # 存储模块 7│ └── utils.py # 工具函数 8├── tests/ # 测试 9│ ├── __init__.py 10│ ├── test_task.py 11│ ├── test_manager.py 12│ └── test_storage.py 13├── main.py # 主程序入口 14├── README.md 15└── requirements.txt 3. 核心模块实现 3.1 任务类(task.py) 1""" 2任务类模块 3- 定义Task类表示单个任务 4- 应用:类、属性、方法 5""" 6 7from datetime import datetime 8from enum import Enum 9 10class Priority(Enum): 11 """ 12 任务优先级枚举 13 - 使用Enum定义常量 14 - 避免魔法数字 15 """ 16 LOW = 1 17 MEDIUM = 2 18 HIGH = 3 19 20class Category(Enum): 21 """任务分类""" 22 WORK = "工作" 23 LIFE = "生活" 24 STUDY = "学习" 25 OTHER = "其他" 26 27class Task: 28 """ 29 任务类 30 31 Attributes: 32 title: 任务标题 33 description: 任务描述 34 priority: 优先级 35 category: 分类 36 due_date: 截止日期 37 completed: 是否完成 38 created_at: 创建时间 39 """ 40 41 def __init__(self, title, description="", 42 priority=Priority.MEDIUM, 43 category=Category.OTHER, 44 due_date=None): 45 """ 46 初始化任务 47 48 Args: 49 title: 任务标题(必需) 50 description: 任务描述 51 priority: 优先级(默认中等) 52 category: 分类(默认其他) 53 due_date: 截止日期(datetime对象) 54 55 Raises: 56 ValueError: 标题为空时 57 """ 58 if not title: 59 raise ValueError("任务标题不能为空") 60 61 self.title = title 62 self.description = description 63 self.priority = priority 64 self.category = category 65 self.due_date = due_date 66 self.completed = False 67 self.created_at = datetime.now() 68 69 def mark_complete(self): 70 """标记任务完成""" 71 self.completed = True 72 73 def mark_incomplete(self): 74 """标记任务未完成""" 75 self.completed = False 76 77 def to_dict(self): 78 """ 79 转换为字典(用于JSON序列化) 80 81 Returns: 82 dict: 任务的字典表示 83 """ 84 return { 85 "title": self.title, 86 "description": self.description, 87 "priority": self.priority.value, 88 "category": self.category.value, 89 "due_date": self.due_date.isoformat() if self.due_date else None, 90 "completed": self.completed, 91 "created_at": self.created_at.isoformat() 92 } 93 94 @classmethod 95 def from_dict(cls, data): 96 """ 97 从字典创建任务(用于JSON反序列化) 98 99 Args: 100 data: 任务字典 101 102 Returns: 103 Task: 任务对象 104 """ 105 task = cls( 106 title=data["title"], 107 description=data.get("description", ""), 108 priority=Priority(data.get("priority", 2)), 109 category=Category(data.get("category", "其他")) 110 ) 111 112 if data.get("due_date"): 113 task.due_date = datetime.fromisoformat(data["due_date"]) 114 115 task.completed = data.get("completed", False) 116 task.created_at = datetime.fromisoformat(data["created_at"]) 117 118 return task 119 120 def __str__(self): 121 """字符串表示""" 122 status = "✓" if self.completed else " " 123 priority_symbols = { 124 Priority.LOW: "▽", 125 Priority.MEDIUM: "◇", 126 Priority.HIGH: "▲" 127 } 128 129 return f"[{status}] {priority_symbols[self.priority]} {self.title}" 130 131 def __repr__(self): 132 """开发者表示""" 133 return f"Task(title='{self.title}', completed={self.completed})" 3.2 存储模块(storage.py) 1""" 2存储模块 3- 负责任务数据的持久化 4- 应用:文件操作、JSON、异常处理 5""" 6 7import json 8import os 9from pathlib import Path 10from typing import List 11from task import Task 12 13class TaskStorage: 14 """ 15 任务存储类 16 - 使用JSON格式存储 17 - 包含完整的异常处理 18 """ 19 20 def __init__(self, file_path="tasks.json"): 21 """ 22 初始化存储 23 24 Args: 25 file_path: 存储文件路径 26 """ 27 self.file_path = Path(file_path) 28 self._ensure_file_exists() 29 30 def _ensure_file_exists(self): 31 """确保存储文件存在""" 32 if not self.file_path.exists(): 33 self.file_path.write_text("[]", encoding="utf-8") 34 35 def save_tasks(self, tasks: List[Task]): 36 """ 37 保存任务列表 38 39 Args: 40 tasks: 任务列表 41 42 Raises: 43 IOError: 文件写入失败时 44 """ 45 try: 46 data = [task.to_dict() for task in tasks] 47 48 # 使用with确保文件正确关闭 49 with open(self.file_path, "w", encoding="utf-8") as f: 50 json.dump(data, f, ensure_ascii=False, indent=2) 51 52 except Exception as e: 53 raise IOError(f"保存任务失败:{e}") 54 55 def load_tasks(self) -> List[Task]: 56 """ 57 加载任务列表 58 59 Returns: 60 List[Task]: 任务列表 61 62 Raises: 63 IOError: 文件读取失败时 64 """ 65 try: 66 with open(self.file_path, encoding="utf-8") as f: 67 data = json.load(f) 68 69 return [Task.from_dict(item) for item in data] 70 71 except json.JSONDecodeError as e: 72 raise IOError(f"JSON解析失败:{e}") 73 except Exception as e: 74 raise IOError(f"加载任务失败:{e}") 75 76 def backup(self, backup_path=None): 77 """ 78 备份任务数据 79 80 Args: 81 backup_path: 备份文件路径 82 """ 83 if backup_path is None: 84 backup_path = f"{self.file_path}.backup" 85 86 import shutil 87 shutil.copy(self.file_path, backup_path) 3.3 任务管理器(manager.py) 1""" 2任务管理器 3- 核心业务逻辑 4- 应用:类、方法、列表操作 5""" 6 7from typing import List, Optional 8from task import Task, Priority, Category 9from storage import TaskStorage 10 11class TaskManager: 12 """ 13 任务管理器 14 - 管理任务的CRUD操作 15 - 提供搜索、统计等功能 16 """ 17 18 def __init__(self, storage: TaskStorage): 19 """ 20 初始化管理器 21 22 Args: 23 storage: 存储对象 24 """ 25 self.storage = storage 26 self.tasks: List[Task] = [] 27 self.load() 28 29 def load(self): 30 """加载任务""" 31 try: 32 self.tasks = self.storage.load_tasks() 33 except IOError as e: 34 print(f"警告:{e}") 35 self.tasks = [] 36 37 def save(self): 38 """保存任务""" 39 try: 40 self.storage.save_tasks(self.tasks) 41 except IOError as e: 42 print(f"错误:{e}") 43 44 def add_task(self, task: Task): 45 """添加任务""" 46 self.tasks.append(task) 47 self.save() 48 49 def get_all_tasks(self) -> List[Task]: 50 """获取所有任务""" 51 return self.tasks 52 53 def get_task(self, index: int) -> Optional[Task]: 54 """ 55 获取指定任务 56 57 Args: 58 index: 任务索引 59 60 Returns: 61 Task或None 62 """ 63 if 0 <= index < len(self.tasks): 64 return self.tasks[index] 65 return None 66 67 def complete_task(self, index: int) -> bool: 68 """标记任务完成""" 69 task = self.get_task(index) 70 if task: 71 task.mark_complete() 72 self.save() 73 return True 74 return False 75 76 def delete_task(self, index: int) -> bool: 77 """删除任务""" 78 if 0 <= index < len(self.tasks): 79 self.tasks.pop(index) 80 self.save() 81 return True 82 return False 83 84 def search_tasks(self, keyword: str) -> List[Task]: 85 """ 86 搜索任务 87 88 Args: 89 keyword: 关键词 90 91 Returns: 92 匹配的任务列表 93 """ 94 keyword = keyword.lower() 95 return [ 96 task for task in self.tasks 97 if keyword in task.title.lower() 98 or keyword in task.description.lower() 99 ] 100 101 def get_statistics(self) -> dict: 102 """ 103 获取统计信息 104 105 Returns: 106 统计字典 107 """ 108 total = len(self.tasks) 109 completed = sum(1 for task in self.tasks if task.completed) 110 pending = total - completed 111 112 by_priority = { 113 Priority.HIGH: 0, 114 Priority.MEDIUM: 0, 115 Priority.LOW: 0 116 } 117 118 for task in self.tasks: 119 if not task.completed: 120 by_priority[task.priority] += 1 121 122 return { 123 "total": total, 124 "completed": completed, 125 "pending": pending, 126 "high_priority": by_priority[Priority.HIGH], 127 "medium_priority": by_priority[Priority.MEDIUM], 128 "low_priority": by_priority[Priority.LOW] 129 } 3.4 主程序(main.py) 1""" 2主程序入口 3- 命令行交互界面 4- 应用:函数、控制流程、异常处理 5""" 6 7from task import Task, Priority, Category 8from manager import TaskManager 9from storage import TaskStorage 10from datetime import datetime 11 12def print_menu(): 13 """打印菜单""" 14 print("\n" + "=" * 50) 15 print("任务管理器") 16 print("=" * 50) 17 print("1. 查看所有任务") 18 print("2. 添加任务") 19 print("3. 完成任务") 20 print("4. 删除任务") 21 print("5. 搜索任务") 22 print("6. 统计信息") 23 print("0. 退出") 24 print("=" * 50) 25 26def display_tasks(tasks): 27 """显示任务列表""" 28 if not tasks: 29 print("没有任务") 30 return 31 32 print("\n任务列表:") 33 for i, task in enumerate(tasks): 34 print(f"{i}. {task}") 35 if task.due_date: 36 print(f" 截止:{task.due_date.strftime('%Y-%m-%d')}") 37 38def add_task_interactive(manager: TaskManager): 39 """交互式添加任务""" 40 print("\n添加新任务") 41 42 title = input("标题:").strip() 43 if not title: 44 print("标题不能为空") 45 return 46 47 description = input("描述(可选):").strip() 48 49 print("优先级:1-低 2-中 3-高") 50 priority_input = input("选择(默认2):").strip() or "2" 51 priority = Priority(int(priority_input)) 52 53 try: 54 task = Task( 55 title=title, 56 description=description, 57 priority=priority 58 ) 59 manager.add_task(task) 60 print("✓ 任务已添加") 61 62 except ValueError as e: 63 print(f"错误:{e}") 64 65def main(): 66 """主函数""" 67 print("欢迎使用任务管理器") 68 69 # 初始化 70 storage = TaskStorage() 71 manager = TaskManager(storage) 72 73 while True: 74 print_menu() 75 choice = input("\n请选择操作:").strip() 76 77 try: 78 if choice == "1": 79 display_tasks(manager.get_all_tasks()) 80 81 elif choice == "2": 82 add_task_interactive(manager) 83 84 elif choice == "3": 85 display_tasks(manager.get_all_tasks()) 86 index = int(input("请输入任务编号:")) 87 if manager.complete_task(index): 88 print("✓ 任务已完成") 89 else: 90 print("无效的任务编号") 91 92 elif choice == "4": 93 display_tasks(manager.get_all_tasks()) 94 index = int(input("请输入任务编号:")) 95 if manager.delete_task(index): 96 print("✓ 任务已删除") 97 else: 98 print("无效的任务编号") 99 100 elif choice == "5": 101 keyword = input("请输入搜索关键词:") 102 results = manager.search_tasks(keyword) 103 display_tasks(results) 104 105 elif choice == "6": 106 stats = manager.get_statistics() 107 print(f"\n总任务数:{stats['total']}") 108 print(f"已完成:{stats['completed']}") 109 print(f"待完成:{stats['pending']}") 110 print(f" 高优先级:{stats['high_priority']}") 111 print(f" 中优先级:{stats['medium_priority']}") 112 print(f" 低优先级:{stats['low_priority']}") 113 114 elif choice == "0": 115 print("再见!") 116 break 117 118 else: 119 print("无效的选择") 120 121 except Exception as e: 122 print(f"发生错误:{e}") 123 import traceback 124 traceback.print_exc() 125 126if __name__ == "__main__": 127 main() 4. 单元测试 1# tests/test_task.py 2import unittest 3from datetime import datetime 4from task import Task, Priority, Category 5 6class TestTask(unittest.TestCase): 7 """测试Task类""" 8 9 def test_task_creation(self): 10 """测试任务创建""" 11 task = Task("测试任务") 12 13 self.assertEqual(task.title, "测试任务") 14 self.assertFalse(task.completed) 15 self.assertEqual(task.priority, Priority.MEDIUM) 16 17 def test_empty_title_raises_error(self): 18 """测试空标题抛出异常""" 19 with self.assertRaises(ValueError): 20 Task("") 21 22 def test_mark_complete(self): 23 """测试标记完成""" 24 task = Task("测试") 25 task.mark_complete() 26 27 self.assertTrue(task.completed) 28 29 def test_to_dict_and_from_dict(self): 30 """测试序列化/反序列化""" 31 task = Task("测试", priority=Priority.HIGH) 32 33 data = task.to_dict() 34 restored = Task.from_dict(data) 35 36 self.assertEqual(restored.title, task.title) 37 self.assertEqual(restored.priority, task.priority) 38 39if __name__ == "__main__": 40 unittest.main() 5. 知识点回顾 这个项目综合运用了: ...

2025-03-20 · 8 min · 1551 words · 老墨

Python教程16:数据结构综合实战

Python 教程 16:数据结构综合实战 “纸上得来终觉浅,绝知此事要躬行。” 学习了列表、元组、字典、集合后,让我们通过一个实际项目——学生成绩管理系统,把这些知识串起来,体会如何选择和组合不同的数据结构。 1. 项目需求 开发一个学生成绩管理系统,功能包括: 添加学生信息 录入和修改成绩 查询学生成绩 统计分析(平均分、最高分等) 筛选功能(如成绩优秀的学生) 2. 数据结构设计 方案选择 1# 学生信息:用字典 2student = { 3 "id": "2024001", 4 "name": "张三", 5 "age": 18, 6 "scores": { # 成绩:嵌套字典 7 "数学": 95, 8 "英语": 88, 9 "物理": 92 10 } 11} 12 13# 所有学生:用列表存储 14students = [ 15 {"id": "2024001", "name": "张三", "age": 18, "scores": {"数学": 95}}, 16 {"id": "2024002", "name": "李四", "age": 19, "scores": {"数学": 88}}, 17] 18 19# 学号索引:用字典加速查找 20students_by_id = { 21 "2024001": students[0], 22 "2024002": students[1], 23} 3. 核心功能实现 3.1 学生管理类 1class StudentManager: 2 """学生成绩管理系统""" 3 4 def __init__(self): 5 self.students = {} # {学号: 学生信息} 6 7 def add_student(self, student_id, name, age): 8 """添加学生""" 9 if student_id in self.students: 10 print(f"学号{student_id}已存在") 11 return False 12 13 self.students[student_id] = { 14 "id": student_id, 15 "name": name, 16 "age": age, 17 "scores": {} 18 } 19 print(f"✓ 添加学生:{name}") 20 return True 21 22 def add_score(self, student_id, subject, score): 23 """添加成绩""" 24 if student_id not in self.students: 25 print(f"学号{student_id}不存在") 26 return False 27 28 self.students[student_id]["scores"][subject] = score 29 print(f"✓ 录入成绩:{subject} = {score}") 30 return True 31 32 def get_student(self, student_id): 33 """查询学生信息""" 34 return self.students.get(student_id) 35 36 def get_average(self, student_id): 37 """计算学生平均分""" 38 student = self.get_student(student_id) 39 if not student or not student["scores"]: 40 return None 41 42 scores = student["scores"].values() 43 return sum(scores) / len(scores) 44 45 def get_top_students(self, n=5): 46 """获取成绩最好的N名学生""" 47 # 计算每个学生的平均分 48 student_avgs = [] 49 for sid, student in self.students.items(): 50 if student["scores"]: 51 avg = sum(student["scores"].values()) / len(student["scores"]) 52 student_avgs.append((student["name"], avg)) 53 54 # 排序并返回前N名 55 student_avgs.sort(key=lambda x: x[1], reverse=True) 56 return student_avgs[:n] 57 58 def get_subject_stats(self, subject): 59 """统计某科目的成绩分布""" 60 scores = [] 61 for student in self.students.values(): 62 if subject in student["scores"]: 63 scores.append(student["scores"][subject]) 64 65 if not scores: 66 return None 67 68 return { 69 "count": len(scores), 70 "max": max(scores), 71 "min": min(scores), 72 "avg": sum(scores) / len(scores) 73 } 74 75 def find_by_score_range(self, subject, min_score, max_score): 76 """查找某科目分数在指定范围内的学生""" 77 result = [] 78 for student in self.students.values(): 79 if subject in student["scores"]: 80 score = student["scores"][subject] 81 if min_score <= score <= max_score: 82 result.append({ 83 "name": student["name"], 84 "score": score 85 }) 86 return result 3.2 使用示例 1# 创建管理器 2manager = StudentManager() 3 4# 添加学生 5manager.add_student("2024001", "张三", 18) 6manager.add_student("2024002", "李四", 19) 7manager.add_student("2024003", "王五", 18) 8 9# 录入成绩 10manager.add_score("2024001", "数学", 95) 11manager.add_score("2024001", "英语", 88) 12manager.add_score("2024001", "物理", 92) 13 14manager.add_score("2024002", "数学", 87) 15manager.add_score("2024002", "英语", 94) 16manager.add_score("2024002", "物理", 89) 17 18manager.add_score("2024003", "数学", 92) 19manager.add_score("2024003", "英语", 85) 20manager.add_score("2024003", "物理", 95) 21 22# 查询学生 23student = manager.get_student("2024001") 24print(f"\n学生信息:{student['name']}") 25print(f"成绩:{student['scores']}") 26 27# 计算平均分 28avg = manager.get_average("2024001") 29print(f"平均分:{avg:.2f}") 30 31# 获取前3名 32print("\n前3名学生:") 33top_students = manager.get_top_students(3) 34for i, (name, avg) in enumerate(top_students, 1): 35 print(f"{i}. {name}: {avg:.2f}") 36 37# 统计数学成绩 38print("\n数学成绩统计:") 39stats = manager.get_subject_stats("数学") 40print(f"人数:{stats['count']}") 41print(f"最高分:{stats['max']}") 42print(f"最低分:{stats['min']}") 43print(f"平均分:{stats['avg']:.2f}") 44 45# 查找数学成绩90分以上的学生 46print("\n数学90分以上:") 47excellent = manager.find_by_score_range("数学", 90, 100) 48for item in excellent: 49 print(f"{item['name']}: {item['score']}") 4. 数据结构选择分析 为什么用字典存储学生 1# 方案1:列表(不推荐) 2students_list = [...] 3# 查找学生需要遍历:O(n) 4 5# 方案2:字典(推荐) 6students_dict = {"2024001": {...}} 7# 查找学生直接访问:O(1) 选择依据:需要频繁通过学号查找学生,字典更高效。 ...

2024-07-01 · 3 min · 633 words · 老墨

Python教程10:第一个实用程序

Python 教程 10:第一个实用程序 “纸上得来终觉浅,绝知此事要躬行。” 经过前面 9 课的学习,我们已经掌握了 Python 的基础知识。今天,让我们把这些知识串起来,开发一个真正实用的程序:批量文件重命名工具。 1. 项目需求 开发一个命令行工具,能够: 批量重命名文件:支持添加前缀、后缀、替换文本 过滤文件:支持按扩展名、文件名模式过滤 预览模式:先预览修改,确认后再执行 撤销功能:记录操作,支持撤销 这个工具很实用,能解决日常工作中的真实问题。 2. 项目结构 1file_renamer/ 2├── file_renamer.py # 主程序 3├── renamer.py # 核心重命名逻辑 4├── utils.py # 工具函数 5└── history.json # 操作历史记录 3. 核心功能实现 3.1 列出目录中的文件 1# utils.py 2import os 3 4def list_files(directory, extension=None, pattern=None): 5 """ 6 列出目录中的文件 7 8 Args: 9 directory: 目标目录 10 extension: 文件扩展名过滤(如'.txt') 11 pattern: 文件名模式(简单的包含匹配) 12 13 Returns: 14 文件路径列表 15 """ 16 files = [] 17 18 for filename in os.listdir(directory): 19 filepath = os.path.join(directory, filename) 20 21 # 只处理文件,忽略目录 22 if not os.path.isfile(filepath): 23 continue 24 25 # 扩展名过滤 26 if extension and not filename.endswith(extension): 27 continue 28 29 # 文件名模式过滤 30 if pattern and pattern not in filename: 31 continue 32 33 files.append(filepath) 34 35 return files 3.2 重命名逻辑 1# renamer.py 2import os 3import re 4 5class FileRenamer: 6 """文件重命名器""" 7 8 def __init__(self, directory): 9 self.directory = directory 10 self.changes = [] # 记录修改 11 12 def add_prefix(self, files, prefix): 13 """添加前缀""" 14 for filepath in files: 15 dirname = os.path.dirname(filepath) 16 filename = os.path.basename(filepath) 17 new_name = prefix + filename 18 new_path = os.path.join(dirname, new_name) 19 self.changes.append((filepath, new_path)) 20 21 def add_suffix(self, files, suffix): 22 """添加后缀(在扩展名前)""" 23 for filepath in files: 24 dirname = os.path.dirname(filepath) 25 filename = os.path.basename(filepath) 26 name, ext = os.path.splitext(filename) 27 new_name = name + suffix + ext 28 new_path = os.path.join(dirname, new_name) 29 self.changes.append((filepath, new_path)) 30 31 def replace_text(self, files, old_text, new_text): 32 """替换文件名中的文本""" 33 for filepath in files: 34 dirname = os.path.dirname(filepath) 35 filename = os.path.basename(filepath) 36 new_name = filename.replace(old_text, new_text) 37 new_path = os.path.join(dirname, new_name) 38 if filepath != new_path: # 只记录有变化的 39 self.changes.append((filepath, new_path)) 40 41 def preview(self): 42 """预览修改""" 43 if not self.changes: 44 print("没有要修改的文件") 45 return 46 47 print(f"\n将要进行 {len(self.changes)} 项修改:") 48 print("-" * 60) 49 for i, (old, new) in enumerate(self.changes, 1): 50 old_name = os.path.basename(old) 51 new_name = os.path.basename(new) 52 print(f"{i}. {old_name} -> {new_name}") 53 print("-" * 60) 54 55 def execute(self): 56 """执行重命名""" 57 if not self.changes: 58 print("没有要执行的操作") 59 return 60 61 success_count = 0 62 for old_path, new_path in self.changes: 63 try: 64 os.rename(old_path, new_path) 65 success_count += 1 66 except Exception as e: 67 print(f"错误:{old_path} -> {e}") 68 69 print(f"\n成功重命名 {success_count}/{len(self.changes)} 个文件") 70 71 # 保存操作历史 72 self.save_history() 73 74 def save_history(self): 75 """保存操作历史(简化版)""" 76 import json 77 from datetime import datetime 78 79 history_file = "history.json" 80 81 # 读取现有历史 82 history = [] 83 if os.path.exists(history_file): 84 with open(history_file, 'r', encoding='utf-8') as f: 85 history = json.load(f) 86 87 # 添加新记录 88 history.append({ 89 'time': datetime.now().isoformat(), 90 'changes': [(old, new) for old, new in self.changes] 91 }) 92 93 # 保存 94 with open(history_file, 'w', encoding='utf-8') as f: 95 json.dump(history, f, indent=2, ensure_ascii=False) 3.3 主程序 1# file_renamer.py 2#!/usr/bin/env python3 3# -*- coding: utf-8 -*- 4 5""" 6文件批量重命名工具 7 8用法: 9 python file_renamer.py 10""" 11 12import os 13from renamer import FileRenamer 14from utils import list_files 15 16def main(): 17 print("=" * 60) 18 print("文件批量重命名工具") 19 print("=" * 60) 20 21 # 获取目录 22 directory = input("\n请输入目录路径(留空使用当前目录):").strip() 23 if not directory: 24 directory = "." 25 26 if not os.path.exists(directory): 27 print(f"错误:目录 '{directory}' 不存在") 28 return 29 30 # 获取文件过滤条件 31 extension = input("文件扩展名过滤(如.txt,留空跳过):").strip() 32 if not extension: 33 extension = None 34 35 # 列出文件 36 files = list_files(directory, extension) 37 38 if not files: 39 print("没有找到符合条件的文件") 40 return 41 42 print(f"\n找到 {len(files)} 个文件") 43 44 # 创建重命名器 45 renamer = FileRenamer(directory) 46 47 # 操作菜单 48 while True: 49 print("\n请选择操作:") 50 print("1. 添加前缀") 51 print("2. 添加后缀") 52 print("3. 替换文本") 53 print("4. 预览修改") 54 print("5. 执行重命名") 55 print("0. 退出") 56 57 choice = input("\n请输入选择:").strip() 58 59 if choice == "1": 60 prefix = input("请输入前缀:") 61 renamer.add_prefix(files, prefix) 62 print("✓ 已添加前缀规则") 63 64 elif choice == "2": 65 suffix = input("请输入后缀:") 66 renamer.add_suffix(files, suffix) 67 print("✓ 已添加后缀规则") 68 69 elif choice == "3": 70 old_text = input("请输入要替换的文本:") 71 new_text = input("请输入新文本:") 72 renamer.replace_text(files, old_text, new_text) 73 print("✓ 已添加替换规则") 74 75 elif choice == "4": 76 renamer.preview() 77 78 elif choice == "5": 79 renamer.preview() 80 confirm = input("\n确认执行?(y/N):").strip().lower() 81 if confirm == 'y': 82 renamer.execute() 83 break 84 else: 85 print("已取消") 86 87 elif choice == "0": 88 print("再见!") 89 break 90 91 else: 92 print("无效的选择") 93 94if __name__ == "__main__": 95 main() 4. 使用示例 场景 1:照片重命名 假设有一批照片: ...

2024-03-18 · 4 min · 787 words · 老墨