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

“纸上得来终觉浅,绝知此事要躬行。”

经过前面 9 课的学习,我们已经掌握了 Python 的基础知识。今天,让我们把这些知识串起来,开发一个真正实用的程序:批量文件重命名工具

1. 项目需求

开发一个命令行工具,能够:

  1. 批量重命名文件:支持添加前缀、后缀、替换文本
  2. 过滤文件:支持按扩展名、文件名模式过滤
  3. 预览模式:先预览修改,确认后再执行
  4. 撤销功能:记录操作,支持撤销

这个工具很实用,能解决日常工作中的真实问题。

2. 项目结构

file_renamer/
├── file_renamer.py    # 主程序
├── renamer.py         # 核心重命名逻辑
├── utils.py           # 工具函数
└── history.json       # 操作历史记录

3. 核心功能实现

3.1 列出目录中的文件

# utils.py
import os

def list_files(directory, extension=None, pattern=None):
    """
    列出目录中的文件

    Args:
        directory: 目标目录
        extension: 文件扩展名过滤(如'.txt')
        pattern: 文件名模式(简单的包含匹配)

    Returns:
        文件路径列表
    """
    files = []

    for filename in os.listdir(directory):
        filepath = os.path.join(directory, filename)

        # 只处理文件,忽略目录
        if not os.path.isfile(filepath):
            continue

        # 扩展名过滤
        if extension and not filename.endswith(extension):
            continue

        # 文件名模式过滤
        if pattern and pattern not in filename:
            continue

        files.append(filepath)

    return files

3.2 重命名逻辑

# renamer.py
import os
import re

class FileRenamer:
    """文件重命名器"""

    def __init__(self, directory):
        self.directory = directory
        self.changes = []  # 记录修改

    def add_prefix(self, files, prefix):
        """添加前缀"""
        for filepath in files:
            dirname = os.path.dirname(filepath)
            filename = os.path.basename(filepath)
            new_name = prefix + filename
            new_path = os.path.join(dirname, new_name)
            self.changes.append((filepath, new_path))

    def add_suffix(self, files, suffix):
        """添加后缀(在扩展名前)"""
        for filepath in files:
            dirname = os.path.dirname(filepath)
            filename = os.path.basename(filepath)
            name, ext = os.path.splitext(filename)
            new_name = name + suffix + ext
            new_path = os.path.join(dirname, new_name)
            self.changes.append((filepath, new_path))

    def replace_text(self, files, old_text, new_text):
        """替换文件名中的文本"""
        for filepath in files:
            dirname = os.path.dirname(filepath)
            filename = os.path.basename(filepath)
            new_name = filename.replace(old_text, new_text)
            new_path = os.path.join(dirname, new_name)
            if filepath != new_path:  # 只记录有变化的
                self.changes.append((filepath, new_path))

    def preview(self):
        """预览修改"""
        if not self.changes:
            print("没有要修改的文件")
            return

        print(f"\n将要进行 {len(self.changes)} 项修改:")
        print("-" * 60)
        for i, (old, new) in enumerate(self.changes, 1):
            old_name = os.path.basename(old)
            new_name = os.path.basename(new)
            print(f"{i}. {old_name} -> {new_name}")
        print("-" * 60)

    def execute(self):
        """执行重命名"""
        if not self.changes:
            print("没有要执行的操作")
            return

        success_count = 0
        for old_path, new_path in self.changes:
            try:
                os.rename(old_path, new_path)
                success_count += 1
            except Exception as e:
                print(f"错误:{old_path} -> {e}")

        print(f"\n成功重命名 {success_count}/{len(self.changes)} 个文件")

        # 保存操作历史
        self.save_history()

    def save_history(self):
        """保存操作历史(简化版)"""
        import json
        from datetime import datetime

        history_file = "history.json"

        # 读取现有历史
        history = []
        if os.path.exists(history_file):
            with open(history_file, 'r', encoding='utf-8') as f:
                history = json.load(f)

        # 添加新记录
        history.append({
            'time': datetime.now().isoformat(),
            'changes': [(old, new) for old, new in self.changes]
        })

        # 保存
        with open(history_file, 'w', encoding='utf-8') as f:
            json.dump(history, f, indent=2, ensure_ascii=False)

3.3 主程序

# file_renamer.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
文件批量重命名工具

用法:
    python file_renamer.py
"""

import os
from renamer import FileRenamer
from utils import list_files

def main():
    print("=" * 60)
    print("文件批量重命名工具")
    print("=" * 60)

    # 获取目录
    directory = input("\n请输入目录路径(留空使用当前目录):").strip()
    if not directory:
        directory = "."

    if not os.path.exists(directory):
        print(f"错误:目录 '{directory}' 不存在")
        return

    # 获取文件过滤条件
    extension = input("文件扩展名过滤(如.txt,留空跳过):").strip()
    if not extension:
        extension = None

    # 列出文件
    files = list_files(directory, extension)

    if not files:
        print("没有找到符合条件的文件")
        return

    print(f"\n找到 {len(files)} 个文件")

    # 创建重命名器
    renamer = FileRenamer(directory)

    # 操作菜单
    while True:
        print("\n请选择操作:")
        print("1. 添加前缀")
        print("2. 添加后缀")
        print("3. 替换文本")
        print("4. 预览修改")
        print("5. 执行重命名")
        print("0. 退出")

        choice = input("\n请输入选择:").strip()

        if choice == "1":
            prefix = input("请输入前缀:")
            renamer.add_prefix(files, prefix)
            print("✓ 已添加前缀规则")

        elif choice == "2":
            suffix = input("请输入后缀:")
            renamer.add_suffix(files, suffix)
            print("✓ 已添加后缀规则")

        elif choice == "3":
            old_text = input("请输入要替换的文本:")
            new_text = input("请输入新文本:")
            renamer.replace_text(files, old_text, new_text)
            print("✓ 已添加替换规则")

        elif choice == "4":
            renamer.preview()

        elif choice == "5":
            renamer.preview()
            confirm = input("\n确认执行?(y/N):").strip().lower()
            if confirm == 'y':
                renamer.execute()
                break
            else:
                print("已取消")

        elif choice == "0":
            print("再见!")
            break

        else:
            print("无效的选择")

if __name__ == "__main__":
    main()

4. 使用示例

场景 1:照片重命名

假设有一批照片:

IMG_001.jpg
IMG_002.jpg
IMG_003.jpg

使用工具添加前缀"2024vacation",变成:

2024_vacation_IMG_001.jpg
2024_vacation_IMG_002.jpg
2024_vacation_IMG_003.jpg

场景 2:文档整理

文档文件名:

report_draft.doc
report_final.docx
report_review.docx

替换"report"为"年度总结":

年度总结_draft.doc
年度总结_final.docx
年度总结_review.docx

5. 改进方向

这个基础版本还可以继续改进:

  1. 正则表达式支持:更强大的匹配和替换
  2. 序号重命名:按序号重命名文件
  3. 日期戳支持:在文件名中添加日期
  4. 递归目录:处理子目录中的文件
  5. GUI 界面:使用 tkinter 创建图形界面
  6. 撤销功能完善:真正实现撤销

6. 知识点回顾

这个项目用到了我们学过的:

  • 变量和数据类型:字符串、列表、字典
  • 控制流程:if、for、while
  • 函数:模块化设计
  • 字符串操作:替换、分割、拼接
  • 文件操作:os 模块、文件读写
  • 异常处理:try-except
  • 编码规范:遵循 PEP 8

7. 小结

今天我们完成了第一个实用程序!

收获

  • 理解了如何将知识点串联起来
  • 学会了模块化设计
  • 体验了真实的开发流程:需求 → 设计 → 实现 → 测试

这只是开始。接下来的课程会学习更多高级特性,让你能开发更复杂的应用。


练习题

  1. 为程序添加序号重命名功能(如 001、002、003)
  2. 实现"撤销上一次操作"功能
  3. 添加进度条显示(使用 tqdm 库)

思考题

如果要开发一个图形界面版本,你会用什么库?如何设计界面?


本文代码示例


相关阅读