CodeGym /Các khóa học /C# SELF /Thao tác hàng loạt với file

Thao tác hàng loạt với file

C# SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu

Hãy tưởng tượng một kho ảnh trong vài năm — hàng nghìn file trong cùng một thư mục (tình huống thường gặp với người dùng Windows), và giờ bạn có nhiệm vụ: sao chép chỉ các file .jpg được tạo trong năm nay vào một thư mục riêng để xử lý. Hoặc bạn cần đổi tên tất cả báo cáo với tiền tố "old_" để phân biệt phiên bản cũ và mới. Ngay cả khi bạn không làm việc với kho ảnh, thao tác hàng loạt cần thiết trong hầu hết dự án xử lý dữ liệu, log, backup hoặc tự động hóa. Chúng còn xuất hiện trong phỏng vấn nữa!

Thao tác hàng loạt là dịp tốt để làm quen:

  • Vòng lặp và LINQ để duyệt nội dung thư mục
  • Thực hành làm việc với đường dẫn (Path)
  • Những cơ bản về lọc, tìm kiếm, đổi tên theo mẫu
  • Các vấn đề quan trọng: bảo mật, lỗi, ghi đè

Bắt đầu nào — chúc bạn có sức mạnh để dẹp bãi hỗn độn file!

2. Sao chép hàng loạt file

Cách hoạt động: nguyên tắc chung

Với thao tác hàng loạt thường cần:

  1. Lấy danh sách file cần thiết (ví dụ, tất cả .txt trong thư mục)
  2. Với mỗi file thực hiện hành động cần thiết (sao chép, xoá, v.v.)

Điều này có thể thực hiện bằng:

  • Directory.GetFiles() — lấy danh sách file,
  • Vòng lặp foreach — duyệt từng file và thực hiện thao tác.

Ví dụ: sao chép tất cả file .txt từ một thư mục sang thư mục khác


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

// Lấy danh sách tất cả file .txt trong thư mục nguồn
string[] txtFiles = Directory.GetFiles(sourceDir, "*.txt");

foreach (string srcPath in txtFiles)
{
    // Lấy chỉ tên file từ đường dẫn đầy đủ
    string fileName = Path.GetFileName(srcPath);

    // Tạo đường dẫn đầy đủ cho file ở thư mục đích
    string destPath = Path.Combine(destDir, fileName);

    // Sao chép file
    File.Copy(srcPath, destPath, overwrite: true); // overwrite - nếu file đã tồn tại, ghi đè
    Console.WriteLine($"Copied: {fileName}");
}

Console.WriteLine("Tất cả các file .txt đã được sao chép thành công!");

Lưu ý: trong ví dụ này ta dùng filter "*.txt" — đó là pattern tìm tên file giống như trong Windows.

Minh họa (sơ đồ)

+----------------+     [*.txt]     +----------------+
|   C:\Source    | ---filter-----> |   C:\Target    |
| a.txt          |   foreach +     |                |
| b.txt          | --copy--------> | a.txt (copied) |
| c.jpg          |                | b.txt (copied) |
+----------------+                +----------------+
(.jpg files bị bỏ qua, chỉ sao chép .txt)

3. Xóa hàng loạt file

Xóa hàng loạt file là thao tác rất phổ biến. Ví dụ, thường cần dọn file tạm, xóa log cũ hoặc các ảnh jpg không còn cần.

Ví dụ: xóa tất cả file cũ hơn 30 ngày


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

DirectoryInfo di = new DirectoryInfo(dir);
// Lấy tất cả file trong thư mục
foreach (FileInfo file in di.GetFiles())
{
    // Kiểm tra ngày sửa cuối
    if (file.LastWriteTime < DateTime.Now.AddDays(-daysOld))
    {
        file.Delete();
        Console.WriteLine($"Đã xóa: {file.Name}");
    }
}

Mẹo: FileInfo.LastWriteTime — rất tiện cho điều kiện "cũ hơn N ngày".

4. Đổi tên hàng loạt file

Đôi khi cần đổi tên nhiều file theo mẫu. Ví dụ, thêm tiền tố chung, đổi đuôi, hoặc đánh số liên tiếp. Trong .NET cũng làm tương tự — lấy danh sách file, rồi đổi tên bằng File.Move().

Ví dụ: thêm tiền tố "old_" cho tất cả file .docx


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);

    // Đổi tên (về thực chất là di chuyển "vào cùng thư mục với tên mới")
    File.Move(oldPath, newPath);
    Console.WriteLine($"Đã đổi tên: {fileName} -> old_{fileName}");
}

Điểm quan trọng: nếu trong thư mục đã có file với tên mới, sẽ ném exception. Có thể xử lý bằng try-catch nếu cần.

5. Sao chép cả thư mục cùng toàn bộ nội dung

Với thao tác đơn giản trên một thư mục không có hàm "sao chép toàn bộ thư mục" một dòng (Directory.Copy không có — cái này là bẫy!). Cần tự làm:

  1. Tạo thư mục đích (nếu chưa có)
  2. Sao chép tất cả file (xem ví dụ quen thuộc)
  3. Sao chép đệ quy tất cả thư mục con (mỗi thư mục con xử lý như một nhiệm vụ sao chép mới)

Hàm tổng quát: sao chép thư mục đệ quy


using System;
using System.IO;

class Program
{
    static void CopyDirectory(string sourceDir, string destDir, bool overwrite = true)
    {
        // Tạo thư mục nếu chưa có
        Directory.CreateDirectory(destDir);

        // Sao chép tất cả file
        foreach (string filePath in Directory.GetFiles(sourceDir))
        {
            string fileName = Path.GetFileName(filePath);
            string destFile = Path.Combine(destDir, fileName);
            File.Copy(filePath, destFile, overwrite);
        }

        // Sao chép tất cả thư mục con (đệ quy)
        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("Thư mục đã được sao chép thành công!");
    }
}

Sơ đồ khối

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

6. Lọc, tìm kiếm và xử lý hàng loạt file

Giả sử bạn muốn không chỉ duyệt thư mục mà chọn theo nhiều tiêu chí: ví dụ, chỉ ảnh có kích thước lớn hơn 5 MB, và được tạo trong năm 2024! Thường kết hợp LINQ với các lớp hệ thống file sẽ tiện.

Ví dụ: in ra tên các ảnh lớn và mới


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)");
}

Trong ví dụ này ta dùng LINQ để "xâu" các bộ lọc — và viết code thực tế hay gặp trong công việc.

7. Duyệt thư mục lồng nhau: đệ quy và duyệt

Thường cần xử lý không chỉ một thư mục mà tất cả thư mục con (ví dụ, xóa tất cả file tạm trong toàn bộ cấu trúc). Các method lấy file (Directory.GetFilesDirectoryInfo.GetFiles) có biến thể với tham số SearchOption.AllDirectories, giúp làm việc đó cho bạn!

Ví dụ: tìm và xóa tất cả file .tmp trong mọi thư mục con


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

foreach (string file in tmpFiles)
{
    File.Delete(file);
    Console.WriteLine($"Đã xóa: {file}");
}
Console.WriteLine("Tất cả file tạm đã được xóa.");

Lưu ý: Cẩn thận với flag này — nó có thể tìm file trong những cấu trúc lồng nhau rất sâu!

8. Tạo hàng loạt file và thư mục

Đôi khi nhiệm vụ ngược lại — tự động tạo cấu trúc thư mục cần thiết hoặc sinh nhiều file.

Ví dụ: tạo 10 thư mục và 10 file trong mỗi thư mục


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, $"Đây là file số {j} trong thư mục {i}");
    }
}

Console.WriteLine("Các thư mục và file đã được tạo!");

Mẹo: Có thể nhanh chóng dựng hạ tầng test, sinh "dummy" cho test, học tập, v.v.

9. Di chuyển hàng loạt file

Tương tự sao chép, chỉ dùng File.Move thay vì File.Copy. Phù hợp để sắp xếp file theo thư mục.

Ví dụ: sắp xếp file theo phần mở rộng

Giả sử trong thư mục có nhiều file loại khác nhau, và bạn muốn phân loại chúng vào thư mục .jpg, .pdf, .docx, v.v.


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); // không sao nếu đã tồn tại

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

    if (!File.Exists(destPath))
    {
        File.Move(path, destPath);
        Console.WriteLine($"Đã di chuyển: {fileName} -> {destDir}");
    }
    else
    {
        Console.WriteLine($"File đã tồn tại ở {destDir}, bỏ qua: {fileName}");
    }
}

File sẽ "đi vào từng hộp", và bạn sẽ cảm thấy như Marie Kondo phiên bản số hóa.

10. Lỗi thường gặp và những lưu ý khi thao tác hàng loạt

Khi làm việc với không chỉ một file mà hàng trăm, sẽ lộ ra nhiều chi tiết thú vị.

Ví dụ, một số file có thể đang được mở bởi chương trình khác — cố gắng xóa hoặc sao chép sẽ gây lỗi. Cũng hay gặp tình huống trong thư mục đích đã có file cùng tên. Nếu code không dự liệu ghi đè, sẽ ném exception. Đôi khi file hoặc thư mục không có quyền truy cập, hoặc bạn vô tình xóa nhầm thứ quan trọng do đệ quy.

Lỗi kinh điển — thực hiện thao tác mà không xử lý lỗi: nếu giữa chừng có sự cố (ví dụ, thiếu quyền với một file), vòng lặp sẽ dừng và các thao tác sau đó không được thực hiện. Vì vậy để thao tác hàng loạt đáng tin cậy hầu như luôn dùng xử lý ngoại lệ qua try-catch bên trong vòng lặp, để có thể bỏ qua file gặp sự cố và tiếp tục với phần còn lại.

Và đừng quên các tham số ghi đè: nơi nào hợp lý thì dùng overwrite: true trong File.Copy, còn khi đổi tên/di chuyển hãy kiểm tra trước sự tồn tại của file ở đường dẫn đích hoặc áp dụng chiến lược xử lý xung đột (đổi tên kèm hậu tố, bỏ qua, ghi log).

1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Tạo và xóa file
Quản lý file và thư mục
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION