应使用 Directory.GetFiles 获取文件列表,因其返回完整路径数组便于校验排序,而 Directory.EnumerateFiles 延迟执行,重命名时易因目录结构变化导致异常或遗漏。

用 Directory.GetFiles 获取待处理文件列表,别用 Directory.EnumerateFiles 直接遍历重命名
批量重命名的第一步是安全、可控地拿到所有目标文件。用 Directory.GetFiles 能一次性获取完整路径数组,便于后续校验、排序、分批;而 Directory.EnumerateFiles 是延迟执行的迭代器,一旦在遍历中修改文件名(比如重命名),可能触发 System.IO.IOException: 目录名无效 或漏掉刚改名的文件——因为底层目录结构已变,枚举器状态不同步。
实操建议:
- 始终用
Directory.GetFiles+ 显式数组或List,避免边读边改 - 加
SearchOption.AllDirectories前确认是否真要递归——跨子目录重命名极易误伤,建议默认只处理当前目录 - 路径统一用
Path.GetFullPath标准化,防止相对路径导致DirectoryNotFoundException
重命名前必须检查目标文件名是否已存在,File.Exists 不能省
按规律生成新文件名(如 "photo_001.jpg" → "IMG_20240501_001.jpg")时,极容易因原始文件名重复、序号冲突或时间戳相同,导致多个源文件映射到同一个目标名。直接 File.Move 会抛出 IOException: 目标文件已存在,且不自动覆盖。
实操建议:
- 每次生成新路径后,先调
File.Exists(newPath);存在则跳过、报错或自动追加序号(如"IMG_20240501_001_1.jpg") - 不要依赖“原始文件名唯一”来假设新名唯一——用户可能混入同名文件,或时间精度不足(
DateTime.Now.ToString("yyyyMMdd")在秒级内生成多文件就撞车) - 若需覆盖,显式传
true给File.Move(oldPath, newPath, true),但务必记录日志,否则静默覆盖难追溯
File.Move 是原子操作,但跨卷移动会变成复制+删除,慎用
多数人以为 File.Move 只是改个名字,实际行为取决于源和目标是否在同一驱动器。同卷是纯元数据更新(快、安全);跨卷则是先复制再删原文件(慢、占双倍空间、中断即残留)。批量重命名时若没注意路径,可能把 C: emp*.log 错移成 D:rchive*.log,瞬间卡死磁盘。
实操建议:
- 用
Path.GetPathRoot(oldPath) == Path.GetPathRoot(newPath)预检是否同卷,不同卷则直接拒绝或明确提示 - 避免在
newPath中硬写盘符,优先用Path.Combine拼接,方便后期迁移到网络路径或容器环境 - 大文件批量处理时,加
try/catch (IOException ex) when (ex.Message.Contains("access is denied"))捕获权限问题,比泛 catch 更精准
中文路径和特殊字符要提前清理,Path.GetInvalidFileNameChars() 得真用
用户拖进来的文件名常含 *、?、、>、|、冒号、斜杠,甚至控制字符。直接拼进新名会触发 ArgumentException: 路径中具有非法字符。Windows 对文件名长度、结尾空格/点也敏感,File.Move 不会自动修剪。
实操建议:
- 用
new string(Path.GetInvalidFileNameChars().Concat(new[] { ' ', '.', ' ' }).ToArray())构建清洗字符集,别只删\和/ - 对新文件名做
name.TrimEnd(' ', '.', ' '),否则"report .xlsx "会被系统转成"report.xlsx"(Windows 自动截尾) - 长文件名(超 260 字符)需启用长路径支持(
App.config加并开启组策略),否则File.Move直接失败
最麻烦的其实是用户把文件名里的年份写成 2024年,你按规则替换成数字后,又得考虑系统区域设置是否影响 DateTime.Parse 解析——这种隐性依赖,调试时根本看不出问题。











