.Net
标准库中,System.IO
命名空间下包含了和输入/输出操作相关的工具类,包括文件系统操作、文件流、网络流等,这里我们简单介绍其中比较常用的内容。至于网络流相关的内容将在网络编程章节详细介绍。
所谓的Stream流,其实是一种对IO设备进行读写的抽象概念。我们读写的设备可以是磁盘、主存、网卡等,这些设备通常由硬件驱动程序来操作,而.Net
为我们进一步封装了一层,将IO设备的读写操作暴露为统一接口,方便我们使用。C#中,所有的流都继承自Stream
基类。
流有三个基本操作:
Position
属性,每读写一个字节都会让其增加1,我们也可以手动调用Seek
方法设置这个属性实现随机读写FileStream
流用于读写文件系统中的文件,下面代码中,我们从一个文本文件中以字节流方式进行了读取,并打印了其中的内容。
// 开启文件流
FileStream fileStream = File.Open(@"E:\1.txt", FileMode.Open);
// 开辟1024字节的数组
byte[] buffer = new byte[1024];
// 从文件流中读入1024字节数据
fileStream.Read(buffer, 0, buffer.Length);
// 关闭文件流
fileStream.Close();
// 输出读取的内容
System.Text.Encoding encoding = System.Text.Encoding.UTF8;
string str = encoding.GetString(buffer);
Console.WriteLine(str);
上面代码的实现比较简单,只是尝试读取的文件的前1024个字节,如果是一个较大的文件,你可能需要编写一个循环来读取其中的内容。
向文件流写入数据也十分简单,下面是一个例子。
// 开启文件流
FileStream fileStream = File.Open(@"E:/2.txt", FileMode.OpenOrCreate);
// 写入数据
string str = "你好,C#!";
System.Text.Encoding encoding = System.Text.Encoding.UTF8;
byte[] strBytes = encoding.GetBytes(str);
fileStream.Write(strBytes, 0, strBytes.Length);
// 关闭流
fileStream.Close();
代码中,我们将字符串以UTF-8
编码转化为了字节数组,并写入字节流。
我们进行文件和网络IO操作时,一些临时数据可以存储在内存中,以便进一步处理,MemoryStream
用于以流的方式读写一块内存区域。下面例子代码简单演示了其用法。
string str = "你好,C#!";
System.Text.Encoding encoding = System.Text.Encoding.UTF8;
byte[] strBytes = encoding.GetBytes(str);
// 创建内存流
MemoryStream memoryStream = new MemoryStream(new byte[1024]);
// 向内存流写入数据
memoryStream.Write(strBytes, 0, strBytes.Length);
// 将流的读写位置重置
memoryStream.Seek(0, SeekOrigin.Begin);
// 从内存流读取数据
byte[] buf = new byte[1024];
memoryStream.Read(buf, 0, buf.Length);
Console.WriteLine(encoding.GetString(buf));
代码中我们对于同一个内存流,县写入了一部分数据,然后重置了其位置,最后又将数据重新读取了出来。
BufferedStream
需要和FileStream
等基础的流类型配合使用,缓冲流使用了内存缓冲区进行块读写,相比直接使用FileStream
等能够提高流读写的性能。注意这里所说的缓冲区和上面读写流使用的byte[] buf
不是一个层次的概念,缓冲流中的缓冲区是.Net
中更为底层的块读写实现。
// 创建缓冲流
FileStream fileStream = File.Open(@"E:\1.txt", FileMode.Open);
BufferedStream bufferedStream = new BufferedStream(fileStream);
// 读取数据
byte[] buf = new byte[1024];
bufferedStream.Read(buf, 0, buf.Length);
// 关闭流
bufferedStream.Close();
fileStream.Close();
System.Text.Encoding encoding = System.Text.Encoding.UTF8;
string str = encoding.GetString(buf);
Console.WriteLine(str);
默认情况下缓冲流使用的缓冲区为4KB,当然我们也可以手动指定其值,读写大文件时,我们可以将其调大。
NetworkStream
用于从网络套接字(Socket)中读写数据,有关这部分内容将在网络编程章节详细介绍。
如果读写文本文件,上面使用字节流的方式其实很不方便,我们需要以字节数组形式读取,然后将其转换为对应编码的字符串。对于文本文件.Net
提供了StreamReader
和StreamWriter
,支持以文件行或字符数组的方式进行读取,使用字符流读写文本文件就方便很多了。
下面例子代码演示了如何使用StreamReader
和StreamWriter
读写文本文件。
// 创建字符读取流
StreamReader streamReader = new StreamReader(@"E:\1.txt");
// 从字符流中读取一行
string s = streamReader.ReadLine();
Console.WriteLine(s);
// 关闭流
streamReader.Close();
// 创建字符写入流
StreamWriter streamWriter = new StreamWriter(@"E:\1.txt");
// 向流中写入数据(覆盖写入)
streamWriter.WriteLine("ha ha ha ~");
// 关闭流
streamWriter.Close();
我们知道文件系统是操作系统的重要组成部分,文件系统提供了一系列接口用于文件和文件夹的增删改查操作,.Net
也对文件系统的操作进行了便利的封装。
.Net
中File
和Directory
是两个比较重要的工具类,它们被声明为静态类,其中包含了操作文件和目录的许多实用方法,包括移动、复制和删除文件(文件夹)等;此外,FileInfo
和DirectoryInfo
则是对于文件系统中文件和目录的抽象,这两个类则需要基于真正的文件和文件夹创建实例来使用。这两组API提供的功能其实有些重合,我们实际开发中需要根据具体使用场景具体选择。
// 创建文件
File.Create(@"E:\3.txt");
// 判断文件是否存在
bool exists = File.Exists(@"E:\3.txt");
Console.WriteLine(exists);
// 删除文件
File.Delete(@"E:\2.txt");
// 移动文件
File.Move(@"E:\1.txt", @"E:\2.txt");
// 拷贝文件
File.Copy(@"E:\2.txt", @"E:\1.txt");
// 读取文件所有内容
byte[] buf = File.ReadAllBytes(@"E:\2.txt");
System.Text.Encoding encoding = System.Text.Encoding.UTF8;
string str = encoding.GetString(buf);
Console.WriteLine(str);
上面代码实现了对一些文件的操作,至于Directory
其实也是类似的,这里就不多介绍了。
Path
是一个静态类,提供了对文件系统路径的解析、拼接等一系列操作。对于文件路径,相比我们自己手动操作字符串,使用Path
工具类要更加靠谱。
string pathStr = @"E:\1.txt";
// 获取文件名
string filename = Path.GetFileName(pathStr);
// 获取文件名(不带扩展名)
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(pathStr);
// 获取文件的路径名
string directoryName = Path.GetDirectoryName(pathStr);
// 组合路径和文件名为完整的路径
string combinedPath = Path.Combine(@"E:\学习资料", "1.txt");