CodeGym /课程 /C# SELF /批量文件操作

批量文件操作

C# SELF
第 40 级 , 课程 4
可用

1. 介绍

想象一下你有好几年的照片归档——一个文件夹里有几千个文件(Windows 用户的经典场景),现在任务是:把本年度创建的所有 .jpg 文件复制到单独的文件夹去做处理。或者你需要给所有报告文件加上前缀 "old_" 来区分旧版和新版。即便你不做照片归档,批量操作在处理数据、日志、备份或自动化时很常见,面试里也经常考到。

批量操作是练习这些东西的好机会:

  • 用循环和 LINQ 遍历目录内容
  • 熟练操作路径(Path
  • 过滤、搜索、按模板重命名的基础技巧
  • 重要的点:安全、错误、覆盖处理

走起 — 让我们一起把文件混乱整齐起来!

2. 批量复制文件

原理:通用流程

做批量操作通常需要:

  1. 拿到需要的文件列表(比如某个文件夹下所有 .txt
  2. 对每个文件执行需要的操作(复制、删除等)

可以用这些工具实现:

  • Directory.GetFiles() — 获取文件列表,
  • 使用 foreach 循环 — 逐个处理文件。

示例:把一个文件夹里的所有 .txt 文件复制到另一个文件夹


string sourceDir = @"C:\Source";
string destDir = @"C:\Target";

// 获取源文件夹中所有 .txt 文件的列表
string[] txtFiles = Directory.GetFiles(sourceDir, "*.txt");

foreach (string srcPath in txtFiles)
{
    // 从完整路径获取文件名
    string fileName = Path.GetFileName(srcPath);

    // 构建目标文件的完整路径
    string destPath = Path.Combine(destDir, fileName);

    // 复制文件
    File.Copy(srcPath, destPath, overwrite: true); // overwrite - 如果目标已存在,则替换
    Console.WriteLine($"Copied: {fileName}");
}

Console.WriteLine("所有 .txt 文件已成功复制!");

注意:在这个例子里我们用的是过滤器 "*.txt" — 这是一个和 Windows 一样的文件名模式。

可视化(示意)

+----------------+     [*.txt]     +----------------+
|   C:\Source    | ---filter-----> |   C:\Target    |
| a.txt          |   foreach +     |                |
| b.txt          | --copy--------> | a.txt (copied) |
| c.jpg          |                | b.txt (copied) |
+----------------+                +----------------+
(.jpg 文件被忽略,只复制 .txt)

3. 批量删除文件

批量删除文件是非常常见的操作。比如清理临时文件、自动删除旧日志,或把不再需要的 jpg 照片清理掉。

示例:删除所有超过 30 天的文件


string dir = @"C:\MyLogs";
int daysOld = 30;

DirectoryInfo di = new DirectoryInfo(dir);
// 获取文件夹中的所有文件
foreach (FileInfo file in di.GetFiles())
{
    // 检查最后修改时间
    if (file.LastWriteTime < DateTime.Now.AddDays(-daysOld))
    {
        file.Delete();
        Console.WriteLine($"已删除: {file.Name}");
    }
}

小技巧FileInfo.LastWriteTime 非常适合做 “比 N 天旧” 这样的判断。

4. 批量重命名文件

有时候需要按模板批量重命名文件。比如加统一前缀、改扩展名,或者顺序编号。在 .NET 里思路一样:先拿到文件列表,然后用 File.Move() 去重命名(实质是移动到相同文件夹但新名字)。

示例:给所有 .docx 文件加上前缀 "old_"


string dir = @"C:\Reports";

string[] docxFiles = Directory.GetFiles(dir, "*.docx");
foreach (string oldPath in docxFiles)
{
    string dirPath = Path.GetDirectoryName(oldPath)!;
    string fileName = Path.GetFileName(oldPath);
    string newPath = Path.Combine(dirPath, "old_" + fileName);

    // 重命名(实际上是同文件夹内移动到新名字)
    File.Move(oldPath, newPath);
    Console.WriteLine($"已重命名: {fileName} -> old_{fileName}");
}

重要:如果目标文件夹里已经有相同的新名字,会抛出异常。按需在循环里用 try-catch 来处理。

5. 递归复制整个目录及其内容

对单个文件夹的简单操作没问题,但 .NET 没有内建一个一行就能复制整个目录的方法(Directory.Copy 并不存在——这是个坑)。通常需要手动实现:

  1. 创建目标文件夹(如果不存在)
  2. 复制所有文件(参考上面的例子)
  3. 递归复制所有子文件夹(把每个子文件夹当作新的复制任务)

通用函数:递归复制文件夹


using System;
using System.IO;

class Program
{
    static void CopyDirectory(string sourceDir, string destDir, bool overwrite = true)
    {
        // 创建目标文件夹(如果不存在)
        Directory.CreateDirectory(destDir);

        // 复制所有文件
        foreach (string filePath in Directory.GetFiles(sourceDir))
        {
            string fileName = Path.GetFileName(filePath);
            string destFile = Path.Combine(destDir, fileName);
            File.Copy(filePath, destFile, overwrite);
        }

        // 递归复制所有子文件夹
        foreach (string subDir in Directory.GetDirectories(sourceDir))
        {
            string dirName = Path.GetFileName(subDir);
            string destSubDir = Path.Combine(destDir, dirName);
            CopyDirectory(subDir, destSubDir, overwrite);
        }
    }

    static void Main()
    {
        string source = @"C:\Archive2023";
        string target = @"D:\Backup2023";
        CopyDirectory(source, target);
        Console.WriteLine("目录已成功复制!");
    }
}

流程示意

            CopyDirectory(A, B)
               /          \
      copy files        foreach subDir -> CopyDirectory(subDir, destSubDir)

6. 批量过滤、搜索和处理文件

假设你不仅要遍历一个文件夹,还要按多个条件筛选:比如只挑出大小超过 5 MB 且创建于 2024 年的图片。把 LINQ 和文件系统类组合起来很方便。

示例:列出大且新的图片的文件名


string dir = @"C:\Pictures";

var filtered = new DirectoryInfo(dir)
    .GetFiles("*.jpg")
    .Where(f => f.Length > 5_000_000 && f.CreationTime.Year == 2024);

foreach (var file in filtered)
{
    Console.WriteLine($"{file.Name} ({file.Length / 1024 / 1024} MB)");
}

这个例子用 LINQ 串联过滤条件,写出在真实工作中常见的代码。

7. 遍历嵌套文件夹:递归与遍历

经常需要处理不仅是一个目录,而是其所有子目录(比如在整个归档结构里删除临时文件)。获取文件的方法(Directory.GetFilesDirectoryInfo.GetFiles)都支持一个参数 SearchOption.AllDirectories,可以帮你把所有子目录也一起找出来!

示例:在所有子文件夹里查找并删除所有 .tmp 文件


string root = @"D:\BigFolder";
string[] tmpFiles = Directory.GetFiles(root, "*.tmp", SearchOption.AllDirectories);

foreach (string file in tmpFiles)
{
    File.Delete(file);
    Console.WriteLine($"已删除: {file}");
}
Console.WriteLine("所有临时文件已删除。");

注意:对这个标志要小心——它可能会找到非常深层的文件!

8. 批量创建文件和目录

有时任务正好相反——自动生成文件夹结构或批量创建很多文件。

示例:创建 10 个文件夹,每个文件夹里生成 10 个文件


string root = @"C:\GeneratedFolders";

for (int i = 1; i <= 10; i++)
{
    string subDir = Path.Combine(root, $"Folder_{i}");
    Directory.CreateDirectory(subDir);

    for (int j = 1; j <= 10; j++)
    {
        string filePath = Path.Combine(subDir, $"File_{j}.txt");
        File.WriteAllText(filePath, $"这是第 {j} 个文件,位于文件夹 {i}");
    }
}

Console.WriteLine("文件夹和文件已创建!");

小技巧:可以快速搭建测试基础设施,生成测试用的“假数据”用于练习或教学。

9. 批量移动文件

和复制类似,不过用 File.Move 替代 File.Copy。特别适合把文件按规则分类到不同文件夹。

示例:按扩展名对文件进行分类

场景:某个文件夹里堆着各种类型文件,你想把它们按扩展名分到 .jpg.pdf.docx 等文件夹里。


string source = @"C:\Downloads";
string[] files = Directory.GetFiles(source);

foreach (string path in files)
{
    string ext = Path.GetExtension(path).TrimStart('.').ToUpper(); // "JPG", "PDF", "DOCX"
    if (string.IsNullOrEmpty(ext)) ext = "OTHER";
    string destDir = Path.Combine(source, ext);

    Directory.CreateDirectory(destDir); // 不用担心已经存在

    string fileName = Path.GetFileName(path);
    string destPath = Path.Combine(destDir, fileName);

    if (!File.Exists(destPath))
    {
        File.Move(path, destPath);
        Console.WriteLine($"已移动: {fileName} -> {destDir}");
    }
    else
    {
        Console.WriteLine($"目标文件夹已存在同名文件,跳过: {fileName}");
    }
}

文件会被分类到不同“箱子”里,你会感觉自己像数码整理的 Marie Kondo(近藤麻理惠)。

10. 批量操作常见错误和注意事项

当你不是在处理单个文件而是成百上千个时,会冒出各种细节问题。

比如一些文件可能被别的程序打开——尝试删除或复制它们会报错。目标文件夹里可能已经有同名文件。如果不允许覆盖,代码会抛异常。有时文件或文件夹权限不足,或者你不小心递归删除了重要数据。

一个常见错误是完全不做错误处理:如果中间某个文件出问题(比如权限不足),整个循环就中断了,后面的文件都没处理到。所以做可靠的批量操作时,几乎总要在循环内部用 try-catch 捕获异常,遇到问题的文件先记录一下然后跳过,继续处理其他文件。

别忘了考虑覆盖策略:在合适的场景下,可以在 File.Copy 里用 overwrite: true,重命名或移动前先检查目标是否存在,或者采用冲突处理策略(加后缀重命名、跳过、记录日志等)。

1
调查/小测验
创建和删除文件第 40 级,课程 4
不可用
创建和删除文件
文件和目录管理
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION