1. 介绍
直接用流(StreamReader, StreamWriter, FileStream)很有用,尤其当你需要在低层控制读/写过程:比如分块写入或者分片处理大文件。
但在实际开发中,经常只是想知道:“这个文件存在吗?”,“把文件从这儿复制到那儿”,“删除文件”,“获取文件夹里所有 .txt 文件”等。为这些场景,Microsoft 提供了两个通用工具:静态类 File 和 Directory。静态的意思是你不需要创建实例,直接通过类名调用方法。
生活类比
如果流是你拿着笔,一笔一划地写字,那么 File 和 Directory 就像你的办公桌和抽屉:你可以“瞬间”打开抽屉(Directory),从里头拿出纸条(File.ReadAllText),把纸条扔掉(File.Delete),或者直接把整个抽屉挪到房间另一头(Directory.Move)。简单的事用简单的方法。
2. 类 File:它是什么,有什么用
File 提供了一组方便的静态方法,覆盖了常见的文件操作。
最常用的方法
| 方法 | 说明 |
|---|---|
|
检查指定路径的文件是否存在 |
|
把整个文件作为文本读取 |
|
把文件的所有行读入数组 |
|
重写文件并写入文本 |
|
在文件末尾追加文本 |
|
复制文件 |
|
删除文件 |
|
移动或重命名文件 |
|
打开流以进行复杂操作 |
快速示例:检查文件并读取
string filePath = "data.txt";
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath);
Console.WriteLine("文件内容:");
Console.WriteLine(content);
}
else
{
Console.WriteLine("未找到文件!");
}
看到了吗?简单场景下不用手动管理流!
3. 类 Directory:轻松管理文件夹
如果 File 处理单个文件,Directory 则处理文件夹(目录)。
主要任务
| 方法 | 说明 |
|---|---|
|
检查文件夹是否存在 |
|
创建文件夹(会一并创建中间路径) |
|
删除文件夹(可以包含子目录) |
|
获取文件夹中的文件列表 |
|
获取子目录列表 |
|
移动或重命名文件夹 |
|
获取应用的当前工作目录 |
示例:创建文件夹并保存文件
string dirPath = "Results";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
Console.WriteLine("文件夹 'Results' 已创建。");
}
string filePath = Path.Combine(dirPath, "summary.txt");
File.WriteAllText(filePath, "汇总数据:...");
Console.WriteLine($"文件 {filePath} 已写入。");
4. 使用 File 和 Directory 的典型场景
基本方法看起来无聊,除非你看到它们的用例!我们通过几个例子把它们放到实际场景里,同时继续扩展我们的示例应用(假设我们做一个简单的待办事项系统 — todo-list,结果保存在文件里)。
保存数据到文件(替换旧文件)
// 把待办项保存到文件
string[] todos = { "买面包", "给医生打电话", "做 C# 的家庭作业" };
string filePath = "todo.txt";
File.WriteAllLines(filePath, todos);
Console.WriteLine("待办列表已保存。");
从文件加载数据
// 如果文件存在就加载任务
if (File.Exists(filePath))
{
string[] loadedTodos = File.ReadAllLines(filePath);
Console.WriteLine("你的待办列表:");
foreach (var task in loadedTodos)
{
Console.WriteLine("- " + task);
}
}
else
{
Console.WriteLine("你还没有待办列表。");
}
添加新任务(不覆盖文件)
string newTask = "带狗去散步";
File.AppendAllText(filePath, newTask + Environment.NewLine);
Console.WriteLine("任务已添加!");
复制和备份文件
string backupPath = "todo_backup.txt";
File.Copy(filePath, backupPath, overwrite: true);
Console.WriteLine("待办列表的备份已创建。");
移动(或重命名)文件
string archivePath = "todo_archive.txt";
File.Move(filePath, archivePath);
Console.WriteLine("待办列表已归档(重命名)。");
删除文件
if (File.Exists(archivePath))
{
File.Delete(archivePath);
Console.WriteLine("归档已删除 — 释放空间!");
}
5. 操作文件夹:示例
创建目录层级
string path = Path.Combine("Reports", "2024", "June");
Directory.CreateDirectory(path);
Console.WriteLine($"文件夹 {path} 已创建(包含中间目录)。");
获取文件和文件夹列表
string dirPath = "Reports";
if (Directory.Exists(dirPath))
{
string[] files = Directory.GetFiles(dirPath);
Console.WriteLine("文件夹中的文件:");
foreach (var file in files)
{
Console.WriteLine(file);
}
string[] subDirs = Directory.GetDirectories(dirPath);
Console.WriteLine("子目录:");
foreach (var dir in subDirs)
{
Console.WriteLine(dir);
}
}
按掩码过滤文件(仅 txt)
string[] txtFiles = Directory.GetFiles(dirPath, "*.txt");
Console.WriteLine("只有 .txt 文件:");
foreach (var file in txtFiles)
{
Console.WriteLine(file);
}
递归遍历(所有子目录中的文件)
string[] allFiles = Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories);
Console.WriteLine("所有嵌套文件夹中的文件:");
foreach (var file in allFiles)
{
Console.WriteLine(file);
}
移动和删除目录
string from = "OldReports";
string to = "Archive/OldReports";
if (Directory.Exists(from))
{
Directory.Move(from, to);
Console.WriteLine($"文件夹 {from} 已移到 {to}");
}
if (Directory.Exists(to))
{
Directory.Delete(to, recursive: true); // true:删除内部所有内容
Console.WriteLine($"文件夹 {to} 已删除(包含所有内容)。");
}
6. 有用的细节
与路径配合:类 Path
处理文件和文件夹时,经常需要拼接完整路径、组合目录名和文件名、解析扩展名、检查非法字符。为此用辅助类 Path。
示例
string folder = "Results";
string filename = "week1.txt";
string fullPath = Path.Combine(folder, filename); // 安全合并!
Console.WriteLine(fullPath); // "Results/week1.txt"(在 Windows 上可能是 "\")
- 获取文件扩展名: Path.GetExtension(fullPath)
- 获取不带路径的文件名: Path.GetFileName(fullPath)
用 Path.Combine 替代字符串拼接 — 对跨平台应用更安全。
“一次性”方法 vs 流式变体
很多 File 和 Directory 的方法(例如 File.ReadAllText, File.WriteAllText, Directory.GetFiles)都做“整件事”:一次性读取/写入/获取文件列表。非常方便,但不适合超大文件或超大目录,可能会耗尽内存。如果要处理 GB 级别的数据,应该用流类(StreamReader, StreamWriter, FileStream 等)或分块处理。
什么时候用 File/Directory,什么时候用流类
| 场景 | 建议使用 |
|---|---|
| 需要检查文件/文件夹是否存在 | |
| 读取/写入小文件(整体) | |
| 在文件末尾追加文本 | |
| 获取目录中的文件列表 | |
| 复制、删除、移动文件 | |
| 复制/删除整个文件夹 | Directory.Delete/Move/Copy(Copy 需要第三方库) |
| 分块处理非常大的文件 | 流类(StreamReader, FileStream) |
实际意义和使用场景
- 快速加载/保存配置、设置、JSON/XML 文件。
- 日志系统(把日志写入磁盘)。
- 简单的备份工具和数据迁移。
- 扫描多媒体、照片、文档的文件夹。
- 用户场景:加载模板、导入/导出、生成报告等。
面试题里也常会让你用这些类来做事:比如“实现一个函数,计算目录下所有 .txt 文件的总大小”,或“把文件备份到另一个文件夹”等。
快速参考:什么时候用哪个方法
graph TD
A[你想做什么?]
A -->|检查存在性| B[File.Exists 或 Directory.Exists]
A -->|整体读/写文件| C[File.ReadAllText/WriteAllText]
A -->|向文件追加数据| D[File.AppendAllText]
A -->|处理文件夹| E[Directory.CreateDirectory, GetFiles, GetDirectories]
A -->|复制/删除/移动文件| F[File.Copy/Delete/Move]
A -->|复制/删除整个文件夹| G[Directory.Delete/Move]
7. 特性和常见错误
存在性检查与“竞态条件”。即使你先检查了文件/文件夹是否存在,在实际操作之前另一个进程也可能改变它们。所以即便检查过,也要用 try-catch 来捕获可能的问题!
权限问题。如果应用没有足够的读/写/删除权限,方法会抛出 UnauthorizedAccessException。
路径过长。路径最大长度(老 Windows 通常是 260 字符;在 .NET 9 中限制放宽了,但仍需注意)。
异常处理。像 File.ReadAllText 或 Directory.GetFiles 这样的“整件事”方法不会容忍错误:如果文件/文件夹不存在就会抛异常。把它们包在 try-catch 中,或者事先检查存在性。
GO TO FULL VERSION