关于《C# 8.0 And .NET Core 3.0 Modern Cross-Platform Development》第九章文件处理部分的翻译记录
关键字:C# 8.0, .NET Core 3.0,Modern Cross-Platform Development, Chapter 09, Working with Files, Streams, and Serialization
管理文件系统
您的应用程序经常需要在不同环境中对文件和目录执行输入和输出。 System和System.IO命名空间包含用于此目的的类。
处理跨平台环境和文件系统
让我们探索如何处理跨平台环境,例如Windows与Linux或macOS之间的差异。
- 在名为Chapter09的文件夹中创建一个名为WorkingWithFileSystems的新控制台应用程序。
- 将工作空间另存为Chapter09并向其中添加WorkingWithFileSystems。
- 导入System.IO名称空间,并静态导入System.Console,System.IO.Directory,System.Environment和System.IO.Path类型,如以下代码所示:
using System.IO; // types for managing the filesystem
using static System.Console;
using static System.IO.Directory;
using static System.IO.Path;
using static System.Environment;
Windows,macOS和Linux的路径不同,因此,我们将从探索.NET Core如何处理此问题开始。
- 创建一个静态OutputFileSystemInfo方法,并编写语句以执行以下操作:
- 输出路径和目录分隔符
- 输出当前目录的路径
- 输出系统文件,临时文件和文档的一些特殊路径
static void OutputFileSystemInfo()
{
WriteLine("{0,-33} {1}", "Path.PathSeparator", PathSeparator);
WriteLine("{0,-33} {1}", "Path.DirectorySeparatorChar", DirectorySeparatorChar);
WriteLine("{0,-33} {1}", "Directory.GetCurrentDirectory()", GetCurrentDirectory());
WriteLine("{0,-33} {1}", "Environment.CurrentDirectory", CurrentDirectory);
WriteLine("{0,-33} {1}", "Environment.SystemDirectory", SystemDirectory);
WriteLine("{0,-33} {1}", "Path.GetTempPath()", GetTempPath());
WriteLine("GetFolderPath(SpecialFolder");
WriteLine("{0,-33} {1}", " .System)", GetFolderPath(SpecialFolder.System));
WriteLine("{0,-33} {1}", " .ApplicationData)",
GetFolderPath(SpecialFolder.ApplicationData));
WriteLine("{0,-33} {1}", " .MyDocuments)", GetFolderPath(SpecialFolder.MyDocuments));
WriteLine("{0,-33} {1}", " .Personal)", GetFolderPath(SpecialFolder.Personal));
}
环境类型还有许多其他有用的成员,包括GetEnvironmentVariables方法以及OSVersion和ProcessorCount属性。
- 在Main方法中,调用OutputFileSystemInfo,如以下代码所示:
static void Main(string[] args)
{
OutputFileSystemInfo();
}
- 在Windows上运行时,运行控制台应用程序并查看结果,如以下屏幕截图所示:
Windows使用反斜杠作为目录分隔符。 macOS和Linux使用正斜杠表示目录分隔符。
管理驱动器
要管理驱动器,请使用DriveInfo,该驱动器具有静态方法,该方法返回有关连接到计算机的所有驱动器的信息。 每个驱动器都有一个驱动器类型。
- 创建WorkWithDrives方法,并编写语句以获取所有驱动器并输出其名称,类型,大小,可用空间和格式,但前提是驱动器已就绪,如以下代码所示:
static void WorkWithDrives()
{
WriteLine("{0,-30} | {1,-10} | {2,-7} | {3,18} | {4,18}",
"NAME", "TYPE", "FORMAT", "SIZE (BYTES)", "FREE SPACE");
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
if (drive.IsReady)
{
WriteLine(
"{0,-30} | {1,-10} | {2,-7} | {3,18:N0} | {4,18:N0}",
drive.Name, drive.DriveType, drive.DriveFormat,
drive.TotalSize, drive.AvailableFreeSpace);
}
else
{
WriteLine("{0,-30} | {1,-10}", drive.Name, drive.DriveType);
}
}
}
优化提示:在读取诸如TotalSize之类的属性之前,请检查驱动器是否已准备就绪,否则您将看到可移动驱动器引发异常。
- 在Main中,注释掉先前的方法调用,并添加对WorkWithDrives的调用,如以下代码所示:
static void Main(string[] args)
{
//OutputFileSystemInfo();
WorkWithDrives();
}
- 运行控制台应用程序并查看结果,如以下屏幕截图所示:
管理目录
要管理目录请使用Directory,Path和Environment静态类。 这些类型包括用于处理文件系统的许多属性和方法,如下图所示:
在构造自定义路径时,必须小心地编写代码,这样它就不会对平台做出任何假设,例如,目录分隔符应该使用什么。
- 创建一个workwithcategories方法,并编写执行以下操作的语句:
- 通过为目录名称创建字符串数组,然后将它们与Path类型的静态Combine方法正确组合,在用户的主目录下定义自定义路径。
- 使用Directory类的静态Exists方法检查自定义目录路径的存在。
- 使用Directory类的静态CreateDirectory和Delete方法创建并删除目录,包括其中的文件和子目录。
static void WorkWithDirectories()
{
// define a directory path for a new folder
// starting in the user's folder
var newFolder = Combine(GetFolderPath(SpecialFolder.Personal), "Code", "Chapter09", "NewFolder");
WriteLine($"Working with: {newFolder}");
// check if it exists
WriteLine($"Does it exist? {Exists(newFolder)}");
// create directory
WriteLine("Creating it...");
CreateDirectory(newFolder);
WriteLine($"Does it exist? {Exists(newFolder)}");
Write("Confirm the directory exists, and then press ENTER: ");
ReadLine();
// delete directory
WriteLine("Deleting it...");
Delete(newFolder, recursive: true);
WriteLine($"Does it exist? {Exists(newFolder)}");
}
- 在Main方法中,注释掉先前的方法调用,并添加对WorkWithDirectories的调用,如以下代码所示:
static void Main(string[] args)
{
//OutputFileSystemInfo();
//WorkWithDrives();
WorkWithDirectories();
}
- 运行控制台应用程序并查看结果,并使用您喜欢的文件管理工具确认目录已创建,然后按Enter删除它,如以下输出所示:
Working with: /Users/markjprice/Code/Chapter09/NewFolder
Does it exist? False
Creating it...
Does it exist? True
Confirm the directory exists, and then press ENTER:
Deleting it...
Does it exist? False
管理文件
处理文件时,可以像导入目录类型一样静态导入文件类型,但是在下一个示例中,我们不会这样做,因为它具有与目录类型相同的方法并且它们会冲突。 The File type has a short enough name not to matter in this case.
- 创建一个WorkWithFiles方法,并编写执行以下操作的语句:
- 检查文件是否存在
- 创建一个文本文件
- 在文件中写一行文本
- 关闭文件以释放系统资源和文件锁(通常在try-finally语句块内执行此操作,以确保即使在写入文件时发生异常,也可以关闭文件)
- 将复制一份作为备份
- 删除原始文件
- 读取备份文件的内容,然后关闭它
static void WorkWithFiles()
{
// define a directory path to output files
// starting in the user's folder
var dir = Combine(GetFolderPath(SpecialFolder.Personal), "Code", "Chapter09", "OutputFiles");
CreateDirectory(dir);
// define file paths
string textFile = Combine(dir, "Dummy.txt");
string backupFile = Combine(dir, "Dummy.bak");
WriteLine($"Working with: {textFile}");
// check if a file exists
WriteLine($"Does it exist? {File.Exists(textFile)}");
// create a new text file and write a line to it
StreamWriter textWriter = File.CreateText(textFile);
textWriter.WriteLine("Hello, C#!");
textWriter.Close(); // close file and release resources
WriteLine($"Does it exist? {File.Exists(textFile)}");
// copy the file, and overwrite if it already exists
File.Copy(sourceFileName: textFile,
destFileName: backupFile, overwrite: true);
WriteLine($"Does {backupFile} exist? {File.Exists(backupFile)}");
Write("Confirm the files exist, and then press ENTER: ");
ReadLine();
// delete file
File.Delete(textFile);
WriteLine($"Does it exist? {File.Exists(textFile)}");
// read from the text file backup
WriteLine($"Reading contents of {backupFile}:");
StreamReader textReader = File.OpenText(backupFile);
WriteLine(textReader.ReadToEnd());
textReader.Close();
}
- 在Main中,注释掉前面的方法调用,并向WorkWithFiles添加一个调用。
- 运行应用程序并查看结果,如下面的输出所示:
Working with: /Users/markjprice/Code/Chapter09/OutputFiles/Dummy.txt
Does it exist? False
Does it exist? True
Does /Users/markjprice/Code/Chapter09/OutputFiles/Dummy.bak exist? True
Confirm the files exist, and then press ENTER:
Does it exist? False
Reading contents of /Users/markjprice/Code/Chapter09/OutputFiles/Dummy.bak:
Hello, C#!
管理路径
有时候,你需要处理路径的一部分;例如,您可能只想提取文件夹名、文件名或扩展名。有时,您需要生成临时文件夹和文件名。您可以使用Path类的静态方法来实现这一点。
- 在WorkWithFiles方法的末尾添加以下语句
// Managing paths
WriteLine($"Folder Name: {GetDirectoryName(textFile)}");
WriteLine($"File Name: {GetFileName(textFile)}");
WriteLine("File Name without Extension: {0}", GetFileNameWithoutExtension(textFile));
WriteLine($"File Extension: {GetExtension(textFile)}");
WriteLine($"Random File Name: {GetRandomFileName()}");
WriteLine($"Temporary File Name: {GetTempFileName()}");
- 运行应用程序并查看结果,如下面的输出所示:
Folder Name: /Users/markjprice/Code/Chapter09/OutputFiles
File Name: Dummy.txt
File Name without Extension: Dummy
File Extension: .txt
Random File Name: u45w1zki.co3
Temporary File Name:
/var/folders/tz/xx0y_wld5sx0nv0fjtq4tnpc0000gn/T/tmpyqrepP.tmp
GetTempFileName会创建一个零字节的文件并返回其名称供您使用,实际上他只返回了一个预备的文件名而不会创建文件。
获取文件信息
要获取有关文件或目录的更多信息,例如文件或目录的大小或上次访问的时间,可以创建FileInfo或DirectoryInfo类的实例。
FileInfo和DirectoryInfo都继承自FileSystemInfo,所以它们都有LastAccessTime和Delete这样的成员,如下图所示:
- 让我们编写一些使用FileInfo实例的代码,以便在一个文件上有效地执行多个操作。在WorkWithFiles方法的末尾添加语句,以创建备份文件的FileInfo实例,并将有关该文件的信息写入控制台,如下面的代码所示:
var info = new FileInfo(backupFile);
WriteLine($"{backupFile}:");
WriteLine($"Contains {info.Length} bytes");
WriteLine($"Last accessed {info.LastAccessTime}");
WriteLine($"Has readonly set to {info.IsReadOnly}");
- 运行应用程序并查看结果,如下面的输出所示:
/Users/markjprice/Code/Chapter09/OutputFiles/Dummy.bak:
Contains 11 bytes
Last accessed 26/11/2018 09:08:26
Has readonly set to False
字节数在您的操作系统上可能会有所不同,因为操作系统可以使用不同的行尾。
控制如何处理文件
使用文件时通常需要控制文件的打开方式。 File.Open方法具有重载功能,可使用枚举值指定其他选项。 枚举类型如下:
- FileMode:控制您要对文件执行的操作,例如CreateNew,OpenOrCreate或Truncate。
- FileAccess:这控制您需要什么访问级别,例如ReadWrite。
- FileShare:此控件可锁定文件,以允许其他进程指定访问级别,例如Read。
您可能想要打开一个文件并从中读取文件,并允许其他进程也读取它,如以下代码所示:
FileStream file = File.Open(pathToFile, FileMode.Open, FileAccess.Read, FileShare.Read);
还有一个文件属性的枚举,如下所示:
- FileAttributes:这是检查FileSystemInfo派生的types Attributes属性的值,例如Archive和Encrypted。
您可以检查文件或目录属性,如以下代码所示:
var info = new FileInfo(backupFile);
WriteLine("Is the backup file compressed? {0}", info.Attributes.HasFlag(FileAttributes.Compressed));