用 Python 批量改文件名

文件批量重命名是常见需求,本文介绍几种实用的 Python 方案。

1. 基本重命名

使用 pathlib

from pathlib import Path

folder = Path("images")

for p in folder.glob("*.png"):
    new_name = p.stem.replace(" ", "_") + ".png"
    p.rename(p.parent / new_name)

使用 os.rename

import os

folder = "images"

for filename in os.listdir(folder):
    if filename.endswith(".png"):
        old_path = os.path.join(folder, filename)
        new_name = filename.replace(" ", "_")
        new_path = os.path.join(folder, new_name)
        os.rename(old_path, new_path)

2. 常用重命名模式

添加前缀

from pathlib import Path

folder = Path("documents")

for p in folder.iterdir():
    if p.is_file():
        p.rename(p.parent / f"backup_{p.name}")

添加序号

from pathlib import Path

folder = Path("photos")

for i, p in enumerate(sorted(folder.glob("*.jpg")), start=1):
    new_name = f"img_{i:03d}{p.suffix}"
    p.rename(p.parent / new_name)
    # img_001.jpg, img_002.jpg, ...

统一扩展名

from pathlib import Path

folder = Path("documents")

for p in folder.glob("*.JPEG"):
    p.rename(p.with_suffix(".jpg"))

3. 使用正则表达式

import re
from pathlib import Path

folder = Path("files")

for p in folder.glob("*"):
    if p.is_file():
        # 移除日期前缀 2024-01-01_
        new_name = re.sub(r'^\d{4}-\d{2}-\d{2}_', '', p.name)
        if new_name != p.name:
            p.rename(p.parent / new_name)

4. 批量重命名函数

import os
from pathlib import Path
import re

def batch_rename(
    folder: str,
    pattern: str = "*",
    rename_func=None,
    dry_run: bool = True
):
    """批量重命名文件
    
    Args:
        folder: 文件夹路径
        pattern: 文件匹配模式
        rename_func: 重命名函数,接受文件名返回新文件名
        dry_run: True 则只打印不实际执行
    """
    folder = Path(folder)
    results = []
    
    for p in folder.glob(pattern):
        if not p.is_file():
            continue
            
        new_name = rename_func(p.name)
        
        if new_name and new_name != p.name:
            results.append((p.name, new_name))
            
            if not dry_run:
                p.rename(p.parent / new_name)
                print(f"重命名: {p.name} -> {new_name}")
    
    return results


# 使用示例:去除空格
batch_rename("images", "*.png", lambda name: name.replace(" ", "_"))

# 使用示例:转小写
batch_rename("images", "*.png", lambda name: name.lower())

# 使用示例:添加日期前缀
from datetime import datetime
batch_rename("files", "*", 
    lambda name: f"{datetime.now().strftime('%Y%m%d')}_{name}")

5. 带冲突处理

from pathlib import Path
from collections import Counter

def safe_rename(folder, pattern, rename_func):
    """安全重命名,处理文件名冲突"""
    folder = Path(folder)
    
    # 第一步:生成所有新文件名
    changes = []
    for p in folder.glob(pattern):
        if p.is_file():
            new_name = rename_func(p.name)
            changes.append((p, new_name))
    
    # 检测冲突
    new_names = [c[1] for c in changes]
    duplicates = [k for k, v in Counter(new_names).items() if v > 1]
    
    if duplicates:
        print(f"警告:以下新文件名会冲突: {duplicates}")
        return
    
    # 执行重命名
    for old_path, new_name in changes:
        if new_name != old_path.name:
            old_path.rename(old_path.parent / new_name)
            print(f"{old_path.name} -> {new_name}")


# 使用
safe_rename("images", "*.png", lambda n: n.replace(" ", "_"))

6. 实际应用示例

整理下载文件

from pathlib import Path
import shutil

downloads = Path("/Users/hao/Downloads")

# 按文件类型分类
for p in downloads.iterdir():
    if not p.is_file():
        continue
    
    ext = p.suffix.lower().replace(".", "")
    dest = downloads / ext
    
    if not dest.exists():
        dest.mkdir()
    
    shutil.move(str(p), str(dest / p.name))
    print(f"移动 {p.name} -> {dest.name}/")

处理照片

from pathlib import Path
from datetime import datetime

photos = Path("photos")

# 从 EXIF 获取拍摄日期(需要 Pillow)
from PIL import Image
from PIL.ExifTags import TAGS

for p in photos.glob("*.jpg"):
    try:
        img = Image.open(p)
        exif = {TAGS[k]: v for k, v in img._getexif().items() if k in TAGS}
        date = exif.get("DateTimeOriginal")
        
        if date:
            dt = datetime.strptime(date, "%Y:%m:%d %H:%M:%S")
            new_name = dt.strftime("%Y%m%d_%H%M%S") + ".jpg"
            p.rename(p.parent / new_name)
            print(f"{p.name} -> {new_name}")
    except:
        continue

7. 完整脚本示例

#!/usr/bin/env python3
"""批量文件重命名工具"""

import argparse
from pathlib import Path


def main():
    parser = argparse.ArgumentParser(description="批量重命名文件")
    parser.add_argument("folder", help="目标文件夹")
    parser.add_argument("--pattern", default="*", help="文件匹配模式")
    parser.add_argument("--replace", help="要替换的字符串")
    parser.add_argument("--with", dest="replacement", help="替换为的字符串")
    parser.add_argument("--prefix", help="添加前缀")
    parser.add_argument("--suffix", help="添加后缀")
    parser.add_argument("--execute", action="store_true", help="实际执行(默认只预览)")
    
    args = parser.parse_args()
    
    folder = Path(args.folder)
    
    for p in folder.glob(args.pattern):
        if not p.is_file():
            continue
        
        name = p.name
        stem = p.stem
        suffix = p.suffix
        
        # 应用转换
        if args.replace:
            new_stem = stem.replace(args.replace, args.replacement or "")
        else:
            new_stem = stem
        
        if args.prefix:
            new_stem = args.prefix + new_stem
        
        if args.suffix:
            new_stem = new_stem + args.suffix
        
        new_name = new_stem + suffix
        
        if new_name != name:
            print(f"{name} -> {new_name}")
            if args.execute:
                p.rename(p.parent / new_name)


if __name__ == "__main__":
    main()

使用:

# 预览
python rename.py images --pattern "*.png" --replace " " --with "_"

# 执行
python rename.py images --pattern "*.png" --replace " " --with "_" --execute

# 添加前缀
python rename.py photos --pattern "*.jpg" --prefix "vacation_"

快速对照表

方法 用途
Path.rename() 基本重命名
shutil.move() 移动文件
Path.glob() 模式匹配
re.sub() 正则替换

Python 批量重命名灵活可控,是处理文件的好帮手。