概述
读写 文件和目录时,可以使用简单的API, 也可以使用 先进的API 来提供更多的功能
- 可以用 简单的API 读写文件
- 可以用 流 得到更多功能,例如:
- 流可以 压缩数据
- 利用 内存映射文件 和 管道 在不同的任务间 共享数据
还必须 区分
Windows.Runtime提供的类 和.NET提供的类
- 在通用Windows平台(UWP)Windows应用程序中:
- 不能 在任意目录中 访问文件系统
- 只能访问 特定的目录
- 可以 让用户选择文件
- 可以混合
Windows.Runtime的类和.NET的类,以同时使用.NET功能和Windows运行库
管理 文件系统
FileSystemInfo: 表示 任何 文件系统对象 的基类FileInfo和File:表示 文件系统上的 文件FileInfo继承自FileSystemInfo
DirectoryInfo和Directory:表示 文件系统上的 文件夹DirectoryInfo继承自FileSystemInfo
Path:其包含的 静态成员 可以用于 处理 路径名DriveInfo:其 属性和方法 提供了 指定 驱动器 的信息
对比
使用哪个类,主要依赖于访问 文件夹或文件的 次数
Directory类和File类- 只包含静态方法,不能被实例化
- 如果只对文件夹或文件执行一个操作,使用这些类就很有用,因为可以省去 创建.NET对象的 系统开销
DirectoryInfo类和FileInfo类- 实现了与
Directory类和File类 大致相同的公共方法 - 还拥有一些公共属性和构造函数
- 成员都不是静态的,需要实例化这些类
- 如果使用同一个对象执行多个操作,这些类就比较有效
- 因为构造时它们将读取 文件系统对象的 身份验证 和 其他信息,无论对对象调用了多少方法,都不需要再读取这些信息
- 实现了与
DriveInfo 检查驱动器信息
在处理 文件和目录 之前,先检查 驱动器 信息
GetDrives()方法 返回DriveInfo数组
public static void Main()
{
DriveInfo[] drives = DriveInfo.GetDrives();
foreach (DriveInfo drive in drives)
{
if (drive.IsReady)
{
Console.WriteLine($"Drive name: {drive.Name}");
Console.WriteLine($"Format: {drive.DriveFormat}");
Console.WriteLine($"Type: {drive.DriveType}");
Console.WriteLine($"Root directory: {drive.RootDirectory}");
Console.WriteLine($"Volume label: {drive.VolumeLabel}");
Console.WriteLine($"Free space: {drive.TotalFreeSpace}");
Console.WriteLine($"Available space: {drive.AvailableFreeSpace}");
Console.WriteLine($"Total size: {drive.TotalSize}");
Console.WriteLine();
}
}
}
输出:
Drive name: C:\
Format: NTFS
Type: Fixed
Root directory: C:\
Volume label: Windows
Free space: 45745283072
Available space: 45745283072
Total size: 128849014784
Mac电脑上,驱动器符 是不可用的。但也可以看到
Ram和Network类型
Drive name: /
Format: hfs
Type: Fixed
Root directory: /
Volume label: /
Free space: 45745283072
Available space: 45745283072
Total size: 128849014784
Drive name: /dev
Format: devs
Type: Ram
Root directory: /dev
Volume label: /
Free space: 0
Available space: 0
Total size: 184832
Drive name: /net
Format: autofs
Type: Network
Root directory: /net
Volume label: /net
Free space: 0
Available space: 0
Total size: 0
Path类 和 Environment.SpecialFolder
Path类 :
- 可以自动添加 缺少的分隔符
- 相比之下,使用字符串拼接 文件路径 很容易遗漏分隔符
- 基于 Windows 和 Unix 处理不同的需求
Path类 :
- 提供一些静态方法:
Path.Combine()- 提供路径的信息
- 以相应的格式显示信息
- 提供一些公共字段, 得到特定于平台的字符:
VolumnSeparatorChar:分隔硬盘(Windows下是 ":")DirectorySeparatorChar:分隔文件夹(Windows下是 "\" 反斜线)AltDirectorySeparatorChar:分隔文件(Windows下是 "/" 正斜线)PathSeparator:分隔多个路径(Windows下是 ";")
- 帮助访问 特定于用户的 临时文件夹
GetTempPath()GetTempFileName()包括文件夹GetRandomFileName()只返回文件名,不包括文件夹
Path.Combine(@"D:\Projects", "ReadMe.txt");
Environment类的 SpecialFolder:
- 是一个 巨大的 枚举
- 提供了许多 文件夹 的值
private static string GetDocumentsFolder() => Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
File类 创建文件
File.WriteAllText()方法
- 文件名 和 写入文件的内容,都在一个调用中指定
const string Sample1FileName = "Sample1.md";
private static void CreateAFile()
{
string fileName = Path.Combine(GetDocumentsFolder(), Sample1FileName);
File.WriteAllText(fileName, "Hello, World!");
}
File类 和 FileInfo类 复制文件
没有复制完整文件夹的方法——必须复制文件夹中的每个文件
File:
File.Copy(fileName1, fileName2);
FileInfo:
var file = new FileInfo(fileName1);
file.CopyTo(fileName2);
实例化 FileInfo 和 DirectoryInfo
给构造函数 传递 文件系统对象的 路径的 字符串
不存在
- 如果路径代表的文件系统对象 不存在,那么 实例化时 不抛出异常
- 而是在 第一次调用某个方法,实际需要相应的文件系统对象时,才抛出异常
可以通过
Exists属性,来检查文件系统对象 是否 存在
不具有适当的类型
还可以通过
Exists属性,来检查文件系统对象 是否 具有适当的类型
- 如果实例化
FileInfo时提供了文件夹路径,则返回false - 如果实例化
DirectoryInfo时提供了文件路径,则返回false - 如果 在这种情况下 调用
FileInfo和DirectoryInfo的 属性和方法, 并不一定会抛出异常- 只有在执行 不可能的 操作时,才抛出异常
- 例如:想用
FileInfo.Open()打开文件夹
- 例如:想用
- 有时 会不抛出异常
- 例如:对一个文件夹,想调用
FileInfo的显示的创建时间的方法 (文件夹也有创建时间,所以不抛异常)
- 例如:对一个文件夹,想调用
- 只有在执行 不可能的 操作时,才抛出异常
var file = new FileInfo(@"C:\Windows");
Console.WriteLine(file.Exists); //输出false
移动和删除 文件和文件夹
FileInfo和DirectoryInfo:
MoveTo()Delete()File和Directory:Move()Delete()
访问 文件属性
File类 提供了 静态方法
FileInfo类提供了 实例方法
FileInformation("./Program.cs");
private static void FileInformation(string fileName)
{
var file = new FileInfo(fileName);
Console.WriteLine($"Name: {file.Name}");
Console.WriteLine($"Directory: {file.DirectoryName}");
Console.WriteLine($"Read only: {file.IsReadOnly}");
Console.WriteLine($"Extension: {file.Extension}");
Console.WriteLine($"Length: {file.Length}");
Console.WriteLine($"Creation time: {file.CreationTime:F}");
Console.WriteLine($"Access time: {file.LastAccessTime:F}");
Console.WriteLine($"File attributes: {file.Attributes}");
}
输出:
Name: Program.cs
Directory: C:\ProfessionalCSharp7-main\ProfessionalCSharp7-main\FilesAndStreams\FilesAndStreamsSamples\WorkingWithFilesAndDirectories\bin\Debug\netcoreapp2.1
Read only: False
Extension: .cs
Length: 7423
Creation time: 2022年4月30日 11:26:05
Access time: 2022年4月30日 11:26:05
File attributes: Archive
修改 文件属性
FileInfo类
- 有一些属性 只有
get访问器,没有set访问器:- 文件名
- 文件扩展名
- 文件的长度
- 有一些属性 有
set访问器:- 创建时间
- 最后一次访问的时间
乍看起来,修改文件属性很奇怪,但这很有用。
- 如果程序只需要读取文件,删除它,再用新内容创建一个新文件。那么就可以选择修改这个文件的创建时间,复用这个文件
private static void ChangeFileProperties()
{
Console.WriteLine(GetDocumentsFolder());
string fileName = Path.Combine(GetDocumentsFolder(), Sample1FileName);
var file = new FileInfo(fileName);
if (!file.Exists)
{
Console.WriteLine($"Create the file {Sample1FileName} before calling this method");
Console.WriteLine("You can do this by invoking this program with the -c argument");
return;
}
Console.WriteLine($"creation time: {file.CreationTime:F}");
file.CreationTime = new DateTime(2025, 12, 24, 15, 0, 0);
Console.WriteLine($"creation time: {file.CreationTime:F}");
}
输出:
creation time: 2022年4月30日 11:41:11
creation time: 2025年12月24日 15:00:00
读写 文件
使用 字符串 读写文件
使用 一个字符串
File.ReadAllTextFile.WriteAllText
每行 使用 一个字符串
File.ReadAllLines: 返回一个 字符串数组- 对 每一行 执行 不同的处理
- 但仍然需要将 完整的文件 读入内存
File.ReadLines: 返回IEnumerable<string>- 逐行读取
- 但 不需要 等所有行 都 读取完
- 在 读取完 之前,就可以遍历
IEnumerable<string>
File.WriteAllLines:- 参数1:文件名
- 参数2:
IEnumerable<string>
File.AppendAllLines:- 把 字符串 追加 到已有文件中
private static void ReadingAFileLineByLine(string fileName)
{
string[] lines = File.ReadAllLines(fileName);
int i = 1;
foreach (var line in lines)
{
Console.WriteLine($"{i++}. {line}");
}
IEnumerable<string> lines2 = File.ReadLines(fileName);
i = 1;
foreach (var line in lines2)
{
Console.WriteLine($"{i++}. {line}");
}
}
private static void WriteAFile()
{
string fileName = Path.Combine(GetDocumentsFolder(), "movies.txt");
string[] movies =
{
"Snow White And The Seven Dwarfs",
"Gone With The Wind",
"Casablanca",
"The Bridge On The River Kwai",
"Some Like It Hot"
};
File.WriteAllLines(fileName, movies);
string[] moreMovies =
{
"Psycho",
"Easy Rider",
"Star Wars",
"The Matrix"
};
File.AppendAllLines(fileName, moreMovies);
}
枚举 文件
处理多个文件时,可以使用
Directory类
Directory.GetFiles()- 返回:目录中 所有文件 的字符串数组
Directory.GetDirectories()- 返回:所有目录 的字符串数组
重载 允许传送搜索模式和
SearchOption
- 搜索模式:
- 不允许 传递 正则表达式
- 只传递 简单表达式
*表示 任意字符?表示 单个字符
SearchOption:AllDirectories: 遍历 所有子目录TopDirectoriesOnly: 留在 顶级目录
遍历 很大的目录 时,
GetFiles()、GetDirectories()在返回之前,需要完整的结果
遍历 很大的目录 时,可以用
EnumerateFiles()、EnumerateDirectories(), 它们使用IEnumerable<string>立即开始 返回结果
private static void DeleteDuplicateFiles(string directory, bool checkOnly)
{
IEnumerable<string> fileNames = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories);
string previousFileName = string.Empty;
foreach (string fileName in fileNames)
{
string previousName = Path.GetFileNameWithoutExtension(previousFileName);
if (!string.IsNullOrEmpty(previousFileName) &&
previousName.EndsWith("Copy") &&
fileName.StartsWith(previousFileName.Substring(0, previousFileName.LastIndexOf(" - Copy"))))
{
var copiedFile = new FileInfo(previousFileName);
var originalFile = new FileInfo(fileName);
if (copiedFile.Length == originalFile.Length)
{
Console.WriteLine($"delete {copiedFile.FullName}");
if (!checkOnly)
{
copiedFile.Delete();
}
}
}
previousFileName = fileName;
}
}