C# IO文件操作
获得文件和文件夹信息
.NET管理文件和文件夹信息的类有如下几个。
Directory和**File**类是两个静态类,可以静态地获取文件和文件夹的信息而无需实例化对象,这在只对某个文件或者文件夹执行一次操作的时候是很有用的。
Path也是一个静态类,为路径操作提供了各种方法。
DirectoryInfo和**FileInfo**类是两个可以实例化的类,在实例化的时候有一些开销,但是实例化之后对某个文件或文件夹进行大量操作的话就基本没有开销了。FileSystemInfo是它们共同的基类。
DriveInfo是一个与驱动器有关的类。
Directory和File类
Directory和File是两个静态类,当只对文件对象执行一次或几次操作的时候很有用。
这里先判断一下C:\Windows\System32\drivers\etc
文件夹是否存在,然后遍历一下其中的内容,然后将hosts文件出来,最后查看一下出来的hosts文件的属性。
const string hostsFolder = @"C:\Windows\System32\drivers\etc";
Console.WriteLine("检查文件夹是否存在");
Console.WriteLine($"{Directory.Exists(hostsFolder)}");
Console.WriteLine("遍历文件夹中的文件");
new List<string>(Directory.EnumerateFiles(hostsFolder)).ForEach((f) => Console.WriteLine(f));
Console.WriteLine($"并获得文件的信息");
string localHostsPath = Path.Combine(Directory.GetCurrentDirectory(), "hosts");
File.Copy(Path.Combine(hostsFolder, "hosts"), localHostsPath, true);
Console.WriteLine($"文件创建时间:{File.GetCreationTime(localHostsPath)}");
Console.WriteLine($"文件最后修改时间:{File.GetLastWriteTime(localHostsPath)}");
FileInfo和DirectoryInfo类
和File、Directory类类似,只不过这两个类需要使用文件对象来实例化。实例化之后,对其进行大量的操作就很方便了。
这里将上面的例子简单地改写了一下。
Console.WriteLine("用DirectoryInfo遍历文件夹的内容");
DirectoryInfo programFiles = new DirectoryInfo(@"C:\Program Files");
new List<FileSystemInfo>(programFiles.GetFileSystemInfos()).ForEach((e) => Console.WriteLine($"文件或文件夹名:{e}"));
Console.WriteLine("");
FileInfo hosts = new FileInfo(@"C:\Windows\System32\drivers\etc\hosts");
Console.WriteLine($"hosts全名:{hosts.FullName}, hosts扩展名:{hosts.Extension}");
Path类
Path类用来执行一些和路径相关的操作,也是一个静态类。
Console.WriteLine();
Console.WriteLine($"目录分隔符:{Path.DirectorySeparatorChar}");
Console.WriteLine($"环境变量分隔符:{Path.PathSeparator}");
Console.WriteLine($"容量分隔符:{Path.VolumeSeparatorChar}");
Console.WriteLine($@"C:\Windows和System连接的结果是:{Path.Combine(@"C:\Windows", "System")}");
DriveInfo类
DriveInfo类可以查看驱动器的相关信息。
DriveInfo c = new DriveInfo("C");
Console.WriteLine("查看驱动器信息\n");
Console.WriteLine($"可用空闲空间:{c.AvailableFreeSpace / Math.Pow(1024, 3)}");
Console.WriteLine($"驱动器格式:{c.DriveFormat}");
Console.WriteLine($"驱动器类型:{c.DriveType}");
Console.WriteLine($"驱动器名称{c.Name}");
Console.WriteLine($"驱动器卷标:{c.VolumeLabel}");
Console.WriteLine($"驱动器总可用空闲空间:{c.TotalFreeSpace / Math.Pow(1024, 3)}");
读写文件
利用File类的扩展方法
File类扩展了几个方法,可以轻松地读写文件,具体有6个方法,分别是ReadAllText方法、ReadAllLines方法、ReadAllBytes方法、WriteAllText方法、WriteAllLines方法和、WriteAllBytes方法。从名字很容易看出来这六个方法的用法。需要注意的是,三个写方法都会覆盖原来的版本,所以如果要实现追加文本内容,就不能用这三个方法。
以下是一个简单的例子,先读取了一下hosts文件的内容(ReadAllText方法),然后利用LINQ和ReadAllLines方法选出注释行(以#开头的行),最后用ReadAllBytes看了一下文件的字节流形式。写入文件也一样简单。
Console.WriteLine("显示hosts文件的部分内容");
const string hosts = @"C:\Windows\System32\drivers\etc\hosts";
Console.WriteLine(File.ReadAllText(hosts).Substring(0, 10));
Console.WriteLine("只显示注释行的前十行");
string[] lines = File.ReadAllLines(hosts);
var comments = from line in lines
where line.StartsWith("#")
select line;
foreach (var comment in comments.Take(10))
{
Console.WriteLine(comment);
}
Console.WriteLine("读取hosts文件的前20个字节");
byte[] bytes = File.ReadAllBytes(hosts);
for (int i = 0; i < Math.Min(20, bytes.Length); ++i)
{
Console.Write(bytes[i]);
}
Console.WriteLine();
const string testFile = "text.txt";
string testString = "123,这是一段测试字符串";
File.WriteAllText(testFile, testString);
Console.WriteLine();
流
流是一种抽象,表示一组信息的顺序集合。流可以是磁盘上的文件,也可以是网络上传过来的数据,或者是内存上的一段区域。
FileStream类
FileStream可以用来读取和写入二进制数据,具体一些就是字节流
FileStream的构造函数需要提供四条信息:要操作的文件、打开文件的模式、访问文件的模式和文件共享的方式。
枚举 | 值 |
---|---|
FileMode | Append、Create、CreateNew、Open、OpenOrCreate、Truncate |
FileAccess | Read、Write、ReadWrite |
FileShare | Delete、Inheritable、None、Read、Write、ReadWrite |
打开文件的模式定义了打开文件的方式,是追加、创建还是打开等等。如果文件的状态和打开文件的模式不匹配,就会发生异常。比如如果文件不存在,追加和打开的模式就会抛出异常。如果文件存在,创建新文件的模式就会抛出异常。
访问文件的方式定义了应该如何访问文件,是读取、写入还是二者都有。
文件共享的方式定义了当一个进程已经占有了文件的时候,另一个进程应该如何访问文件。
FileStream既可以直接从构造函数中创建,也可以由一个FileInfo对象创建。相应的,FileInfo中也有对应的Create方法和Open方法,用来创建对应的文件流。
获得文件流之后,就可以读写数据了。ReadByte方法用于读取一个字节,如果已经到文件的末尾,就会返回-1。Read方法则需要一个缓冲区,并提供起始的位置和要读取的字节数。对应的还有Write和WriteByte方法。
最后不要忘了在使用完文件流对象之后将其关闭。
Console.WriteLine("文件测试:\n");
//不同的方式打开文件流
FileStream testFile = new FileStream("test.txt", FileMode.Create, FileAccess.ReadWrite);
//FileStream testFile2 = new FileInfo("text2.txt").Open(FileMode.OpenOrCreate, FileAccess.ReadWrite);
//写文件
testFile.WriteByte(50);
testFile.Write(new byte[] { 10, 20, 30, 40 }, 0, 4);
testFile.Close();
//读文件
testFile = new FileInfo("test.txt").OpenRead();
byte[] buffer = new byte[testFile.Length];
testFile.Read(buffer, 0, buffer.Length);
new List<byte>(buffer).ForEach((e) => Console.Write(e));
testFile.Close();
StreamReader和StreamWriter
StreamReader和StreamWriter用来进行文本文件的读取和写入。在构造StreamReader和StreamWriter的时候,可以直接用一个文件名来构造,也可以用FileInfo对象或者是FileStream对象来构造,使用后者的好处是可以用FileAccess和FileMode更精细地控制文件的读写权限。
另外,还可以在构造字符流的时候设定其编码,编码是System.Text.Encoding类的几个属性之一,可以是ASCII、Unicode、UTF7、UTF8、UTF32等等值。根据MSDN的介绍,Unicode就是小端编码UTF16。
获取到了字符流对象之后,就可以对其进行读写操作了。有Read、Write、ReadLine、WriteLine等多个方法,它们为不同的参数重载了多个版本,可以满足各种各样的需求。注意在流使用完之后需要关闭。
Console.WriteLine("\n字符流读写测试:\n");
StreamWriter outFile = new StreamWriter("test.txt");
outFile.WriteLine("This is a file");
outFile.WriteLine(DateTime.Now.ToShortDateString());
outFile.Close();
StreamReader inFile = new StreamReader("test.txt");
new List<string>(inFile.ReadToEnd().Split('\n')).ForEach((e) => Console.WriteLine(e));
inFile.Close();