用 re.sub() 替换文件名非法字符最直接:用 r'[/*?:"|]' 匹配 Windows 禁止字符,统一替换成 '_';推荐预编译白名单模式 r'1',strip 首尾空格/点/下划线/短横线,空则设为 'unnamed',并处理连续下划线、Unicode 控制符及扩展名保护。a-zA-Z0-9._- ↩

用 re.sub() 替换文件名中的非法字符最直接
Windows 和某些云存储服务不允许文件名含 *、?、"、、>、|、:、/、,直接用 re.sub() 批量替换最省事。别先想着“保留语义”或“转义”,清洗的第一目标是能成功写入磁盘。
- 用
r'[\/*?:"|]'作为模式,注意反斜杠和斜杠都要转义,正则里写成双反斜杠 - 替换目标统一用下划线
'_',比空字符串更安全(避免连续下划线或开头结尾下划线导致歧义) - 别漏掉
re.escape()的诱惑——它只适合动态拼接模式,静态非法字符集手写正则更快更可控 - 示例:
re.sub(r'[\/*?:"|]', '_', 'report?.xlsx')→'report_.xlsx'
保留字母数字和常见分隔符,用 re.compile() 提升重复调用性能
如果批量处理成百上千个文件名,每次调用 re.sub() 都重新编译正则会拖慢速度。提前用 re.compile() 编译一次,后续复用。
- 推荐白名单思路:
re.compile(r'[^a-zA-Z0-9._-]'),只留大小写字母、数字、点、下划线、短横线 - 点(
.)要保留在文件扩展名里,所以不能无脑删;短横线(-)比空格更适合作为单词分隔符 - 别把空格也放进白名单——空格在路径中容易引发 shell 解析问题,统一换成
'_' - 示例:
clean_pattern = re.compile(r'[^a-zA-Z0-9._-]'); clean_pattern.sub('_', 'My Report (2024).pdf')→'My_Report_2024.pdf'
处理开头结尾空格、点、下划线这些「看不见的坑」
正则替换完不等于名字就安全了。Windows 禁止文件名以空格或点开头/结尾,也禁止纯空格或纯点的名字——这些 re.sub() 不会自动修。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 必须额外调用
.strip(' ._-'),去掉首尾的空格、点、下划线、短横线 - strip 后检查是否为空字符串,空则 fallback 成
'unnamed',否则os.rename()会报OSError: [WinError 123] - 特别注意:多个连续下划线(如
'a__b')虽不报错,但可读性差,可用re.sub(r'_+', '_', name)压缩 - 示例:
name = ' .my-file.. '; name.strip(' ._-') → 'my-file'
跨平台兼容性:Linux/macOS 不报错,但逻辑仍要一致
Linux 允许更多符号,但你写的清洗逻辑大概率会被 Windows 用户或共享目录使用者碰到。别因本地跑通就跳过校验。
- 不要用
os.path.basename()或pathlib.Path.name前就做清洗——先取纯文件名再处理,避免路径分隔符干扰 - 扩展名要单独保留并拼接,防止
.tar.gz被误切成.tar_gz;用os.path.splitext()拆开再组合 - 如果原始名含 Unicode 控制字符(如零宽空格),
re.sub(r'[u200b-u200fu202a-u202f]', '', name)得补上 - 最终建议封装成函数,带
max_length=255截断参数——NTFS 单文件名上限就是 255 字符
事情说清了就结束。真正难的不是正则怎么写,而是清洗后要不要记录原始名映射关系、是否要提示用户哪些字符被替换了——这些得看你的使用场景,库不会替你决定。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










