首页 / C# 入门教程 / 路径与目录操作

C# 入门教程

路径与目录操作

本教程共 100 篇 · 第 94 篇 · 更新于 2026-07-31 · 约 8 分钟阅读

C#C# 入门教程编程语言PathDirectoryIO

94. 路径与目录操作

本节目标:学完能用 Path 写出跨平台的文件路径,并用 Directory 创建、删除、列举目录。

操作文件和目录,第一步是「正确地描述一个路径」。新手最容易犯的错,就是手动拼字符串 "data\\logs\\app.log",换到 Linux 上立刻出错。C# 的 System.IO.PathDirectory 就是为解决这些麻烦设计的。

一、Path:永远别手拼分隔符

不同系统的目录分隔符不同:Windows 用 \,Linux/macOS 用 /Path.Combine 会按当前系统自动选分隔符,这是写路径的唯一正确姿势。

namespace CSharpDemo;

string dir = Path.Combine("data", "logs");
string file = Path.Combine(dir, "app.log");

Console.WriteLine(file);                  // Windows: data\logs\app.log
Console.WriteLine(Path.GetFileName(file));   // app.log
Console.WriteLine(Path.GetExtension(file));  // .log
Console.WriteLine(Path.GetDirectoryName(file)); // data\logs
Tip

Path 是静态类,方法都不碰磁盘,只处理「字符串」。所以拼路径不会报错,真正访问文件才可能被拒绝。获取当前系统分隔符可用 Path.DirectorySeparatorChar

二、从路径里取信息

Path 还能帮你拆出文件名、扩展名、不带扩展名的名字,省得自己写字符串截取:

namespace CSharpDemo;

string p = Path.Combine("temp", "report.csv");
Console.WriteLine(Path.GetFileNameWithoutExtension(p)); // report
Console.WriteLine(Path.GetPathRoot(p));                 // 在 Linux 下为 /
Console.WriteLine(Path.ChangeExtension(p, ".txt"));     // temp/report.txt
Note

Path 的方法都是「纯字符串运算」,不管那个文件是否真存在。它们不会抛「文件找不到」,只有当你真的用 Directory/File 去碰磁盘时才可能出错。所以拼路径阶段可以放心写。

三、Directory:创建与删除

Directory 是静态类,方法直接作用于磁盘。创建目录时即使父目录不存在也会一并建好,且目录已存在也不会报错——对初始化场景很友好。

namespace CSharpDemo;

// 创建(递归建好 data\logs)
Directory.CreateDirectory(Path.Combine("data", "logs"));

if (Directory.Exists("data"))
{
    Console.WriteLine("data 目录存在");
}

// 删除:recursive 为 true 才连子目录一起删
Directory.Delete("data", recursive: true);
Note

Directory.Delete 不写 recursive: true 时,若目录非空会抛异常。删之前想清楚是不是真要连子内容一起清掉。

四、列举目录里的文件

两个常用方法:GetFiles 一次返回全部路径数组;EnumerateFiles 用迭代器逐条给出,处理上百万文件时更省内存。

namespace CSharpDemo;

Directory.CreateDirectory("data");

// 列出 data 下所有 .txt,可加 SearchOption.AllDirectories 搜子目录
foreach (var f in Directory.EnumerateFiles("data", "*.txt"))
{
    Console.WriteLine(f);
}

五、DirectoryInfo:面向实例的写法

如果你想反复操作同一个目录,DirectoryInfo 比静态 Directory 更顺手:它把目录封装成对象,可读取创建时间、大小等属性。

namespace CSharpDemo;

var info = new DirectoryInfo("data");

if (!info.Exists)
{
    info.Create();
}

Console.WriteLine($"创建于:{info.CreationTime}");

foreach (var fi in info.GetFiles())
{
    Console.WriteLine($"{fi.Name} 占用 {fi.Length} 字节");
}
Tip

只做一次操作用静态 Directory 就够了;要多次访问同一个目录的元信息,用 DirectoryInfo 更清晰,也少拼路径。

六、新手踩坑

  1. 硬编码 \\/ 分隔符,程序失去跨平台能力。一律用 Path.Combine
  2. 用相对路径时搞不清「当前目录」在哪。调试阶段先用绝对路径或打印 Directory.GetCurrentDirectory()
  3. 删除目录忘了 recursive: true,非空目录删不动。但反过来,递归删除很危险,先确认路径。
  4. 大目录用 GetFiles 一次性全加载,内存暴涨。换成 EnumerateFiles 惰性遍历。
Warning

Directory.Delete(path, recursive: true) 是「不可逆的硬删除」,不会进回收站。生产代码删目录前,务必先用 Directory.Exists 确认路径没错,最好只删你自己创建的子目录,别拿到根目录或用户家目录去试。一个手滑,重要数据就没了。

补充:Path 常用方法速查

下面这些是最常碰到的 Path 方法,记住它们就够了:

  • Path.Combine(...):跨平台拼接路径,日常最常用。
  • Path.GetFileName(path):取文件名(含扩展名)。
  • Path.GetDirectoryName(path):取文件所在的目录部分。
  • Path.GetExtension(path):取扩展名(含点,如 .log)。
  • Path.GetFileNameWithoutExtension(path):取文件名并去掉扩展名。
  • Path.ChangeExtension(path, ".txt"):换一个扩展名。
  • Path.GetFullPath(path):把相对路径解析成绝对路径,看清它到底指哪。
  • Path.GetTempPath():取系统的临时目录,适合放用完即弃的临时文件。
  • Path.DirectorySeparatorChar:当前系统的目录分隔符,需要时直接读它。
Note

再强调一次:Path 的方法全是「纯字符串运算」,不碰磁盘。所以你可以用它大胆拼、大胆拆,它们不会因「文件不存在」而抛异常。真正的 IO 异常,只在你用 Directory/File 去访问磁盘时才可能发生。

补充:Directory 常用方法速查

目录操作同样有固定套路,常用方法一览:

  • Directory.CreateDirectory(path):创建目录(含缺失的父目录),已存在也不报错。
  • Directory.Exists(path):判断目录是否存在,删之前先确认。
  • Directory.Delete(path, recursive)recursive: true 才连子内容一起删。
  • Directory.GetFiles(path, "*.txt"):返回某目录下匹配的文件路径数组。
  • Directory.EnumerateFiles(path):惰性枚举文件,处理大目录更省内存。
  • Directory.GetDirectories(path):列出子目录路径。
  • Directory.GetCurrentDirectory():取当前工作目录,调试路径问题时很有用。

相对路径还是绝对路径

刚上手时最容易被「相对路径相对谁」绊倒。在 dotnet run 的项目里,相对路径通常相对于「当前工作目录」(一般是你执行命令所在的目录),而不是 .cs 源码所在的目录。这就是「本地跑得好好的,发布后却找不到文件」的常见根源。

经验法则:调试阶段先用 Path.GetFullPath(相对路径) 把真实绝对路径打印出来,确认它指向你以为的位置;正式代码里,需要稳定定位的文件(配置、模板、资源),建议拼成基于程序所在目录或已知根目录的绝对路径,别被「当前目录」的漂移坑到。

Tip

想拿到程序所在目录,可用 AppContext.BaseDirectory;它指向程序集加载的位置,比「当前工作目录」更稳,适合放你的配置文件和资源文件。相对路径适合临时草稿,绝对路径适合正式部署。

Note

路径字符串里若混入了用户输入,要警惕「目录遍历」风险:用户传入 ../../ 可能逃出你预期的目录、去动别的文件。生产代码在处理用户提供的文件名时,应校验最终 GetFullPath 是否仍落在允许的根目录内,避免越权访问。

七、为什么这样设计

为什么要有 Path 这么个「不碰磁盘只拼字符串」的类?因为路径本质上是字符串,但它有一套跨平台的规则:分隔符不同、根路径写法不同、各段能不能有空格不同。把这些规则集中到一个类里,所有代码都通过 Path.Combine 拼路径,就能一处写对、处处跑通,不必在每个拼字符串的地方都操心平台差异。

DirectoryDirectoryInfo 则是「静态便利」与「实例对象」两种风格的并存:前者适合一次性动作,调用即走;后者把目录封装成对象,适合你要反复查询它的属性、在多处引用同一个目录。两者底层一致,只是用法的顺手程度不同。

八、Path / Directory / DirectoryInfo 怎么选

任务首选
拼路径、取文件名/扩展名Path(纯字符串,不碰磁盘)
创建/删除/判断目录存在(一次性)Directory 静态方法
反复操作同一目录、读创建时间等元信息DirectoryInfo
遍历大目录EnumerateFiles/EnumerateDirectories 惰性枚举

九、本节小结

路径交给 Path.Combine 保证跨平台,目录的增删查交给 Directory(一次性)或 DirectoryInfo(反复操作)。下章我们把文件真正读出来、写进去。