在Windows操做系统中,存储介质统称为驱动器,硬盘因为能够划分为多个区域,每个区域称为一个驱动器。.NET Framew ork提供DriveInfo类和 DriveType枚举型,以方便在程序中直接使用驱动器。DriveInfo类的经常使用字段成员有DriveFormat(文件系统格式,如NTFS或FAT32)、DriveType(驱动器类型)、Name(驱动器名)、TotalSize(总空间)、TotalFreeSpace(得到驱动器可用空间)。经常使用的方法成员有GetDrives(得到可用驱动器列表)。windows
DriveType枚举型的枚举值有CDRom(光驱)、Fixed(硬盘)、Network(网络驱动器)和Removeable(软盘或U盘)等。例如,如下代码能够输出每个硬盘驱动器的剩余空间信息。数组
DriveInfo[] drivers = DriveInfo.GetDrives(); foreach(DriveInfo driver in drivers) { if(driver.DriveType == DriveType.Fixed && driver.DriveFormat == "NTFS") { Console.WriteLine("在{0}驱动器上还有{1}字节的剩余空间。", driver.Name, driver.AvailableFreeSpace); } } Console.ReadLine();
为了方便检索文件,须要在驱动器中先建立目录,而后把文件保存到这个目录中。在Windows操做系统中,目录又称文件夹。每一个驱动器都有一个根目录,使用”\”表示,如”C:\”表示C驱动器的根目录。建立在根目录中的目录称为一级子目录。在一级子目录中建立的目录称为二级子目录,依此类推。文件系统的目录结构是一种树形结构。缓存
.NET Framework提供了Directory类和DirectoryInfo类,以方便在程序中直接操做目录。网络
Directory类的经常使用方法成员有CreateDirectory(建立新目录)、Delete(删除目录)、Exists(判断目录是否存在)、Move(移动目录)、GetFiles(得到目录的文件列表)、GetDirectories(得到子目录列表)等。数据结构
DirectoryInfo类的经常使用字段成员有Name(提取目录名)、Exists(判断目录是否存在)、Parent(父目录)、Root(根目录)、MoveTo(移动目录)、GetFiles(得到目录的文件列表)、GetDirectories(得到子目录列表)等。例如,如下代码分别展示了Directory类和DirectoryInfo类的基本方法。异步
Directory.CreateDirectory(@"d:\C#程序设计"); if(Directory.Exists(@"d:\C#程序设计")) { Console.WriteLine("建立成功"); } Directory.Delete(@"d:\C#程序设计"); if (!Directory.Exists(@"d:\C#程序设计")) { Console.WriteLine("删除成功"); } DirectoryInfo dir = new DirectoryInfo(@"d:\C#程序设计"); if (!dir.Exists) { dir.Create(); } else { Console.WriteLine("该目录已经存在"); }
.NET Framework提供了File类和FileInfo类,以方便在程序中直接操做文件。File和FileInfo类位于System.IO命名空间,均可以用来实现建立、复制、移动、打开文件等操做。File类和FileInfo类与Directory类和DirectoryInfo类的工做方式类似。File类是一个静态类,可直接调用其方法成员。FileInfo类不是静态类,须要先建立实例。ide
(一)、文件类File函数
File类的经常使用方法ui
常 用 方 法编码 |
介 绍 |
Open() |
打开文件 |
Create() |
建立文件 |
Copy() |
复制文件 |
Delete() |
删除文件 |
Exists() |
判断文件是否存在 |
Move() |
移动文件 |
Replace() |
替换文件 |
AppendAllText() |
新建文件并添加文本 |
ReadAllText() |
打开并读取文本内容 |
下面经过一个示例演示File类的用法。
(1) 建立一个名为FileCreate的控制台应用程序项目
(2) 修改Program.cs文件中的Main方法的内容以下:
//设置所要建立文件的绝对路径 string path = @"d:\test.txt"; //以路径为参数建立文件 File.Create(path);
代码中变量path给出类文件的路径,利用File类的Create方法建立类该文件。查看D盘根目录,会有一个新的test.txt的文档出现。
(二)、 文件信息类 FileInfo
文件信息类FileInfo与File类不一样,它虽然也提供类建立、复制、删除、移动和打开文件的方法,而且帮助建立FileStream对象,可是它提供的仅仅是实例方法。
FileInfo类经常使用字段
经常使用字段 |
介绍 |
Name |
提取文件名 |
Directory |
所属目录 |
Exists |
是否存在(继承自父类FileSystemInfo) |
Extension |
文件扩展名 |
Length |
文件长度 |
IsReadOnly |
是否为只读 |
FileInfo类经常使用方法
经常使用方法 |
介绍 |
Open() |
打开文件 |
Create() |
建立文件 |
CopyTo() |
复制到新文件 |
Delete() |
删除文件 |
MoveTo() |
移动文件 |
Replace() |
替换文件 |
EnCrypt() |
加密文件 |
Decrypt() |
解密文件 |
所以要使用FileInfo类,必须先实例化一个FileInfo对象。FileInfo类的经常使用方法与File类基本相同。
与文件类File和文件夹类Directory相比,文件信息类FileInfo和文件夹信息类DirectoryInfo具备其大部分功能。
* File类和Directory类适合对不一样的对象进行单一的处理。此种特殊状况下,静态方法的调用速度比较快,不用进行实例化。
* FileInfo类和DirectoryInfo类适合用于对同一文件或文件夹进行多种操做的状况。此种状况下,实例化后的对象不须要每次都寻找文件,能够直接对该文件进行操做。
(三)、 路径
每一个驱动器包含一个或多个目录,而每一个目录又能够包含一个或多个子目录,目录的结构为树形结构。一个文件只能保存在树形结构的某个特定的目录中,文件所在位置为路径。要检索文件时,必须首先肯定文件的路径。路径由驱动器盘符、目录名、文件名、文件扩展名和分隔符组成,有两种种表示方法:一种是从驱动器的根目录开始书写,如C:\Windows\System32\notepad.exe,这种路径称为绝对路径;另外一种是从当前目录位置开始书写,如System32\notepad.exe(假设当前目录为C:\Windows),这种路径称为相对路径。
在C#中,使用文件和目录路径时要十分谨慎。C#将反斜杠”\”字符视做转义符,所以当路径表示为字符串时,要使用两个反斜杠表示,例如:
“C:\\Windows\\System32\\notepad.exe”
另外,C#容许在字符串前添加”@”标志,以提示编译器不要把”\”字符视做转义符,而视做普通字符,例如:
@”C:\Windows\System32\notepad.exe”
.NET Framework提供了Path类,以帮助在程序中管理文件和目录路径,Path类位于System.IO命名空间,是一个静态类,能够用来操做路径的每个字段,如驱动器盘符、目录名、文件名、文件扩展名和分隔符等。Path类的经常使用字段成员有PathSeperator(路径分隔符,如”;”)、DirectorySeparatorChar(目录分隔符,如”\”)、VolumeSeparator(卷分隔符,如”:”)、AltDirectorySeparator(替换目录分隔符,如”/”),经常使用的方法成员有GetDirectoryName(取目录名)、GetFileName(取文件名)、GetExtension(取文件扩展名)、GetFullPath(取完整路径)、GetTempPath(取操做系统的临时文件路径)等,例如,如下代码表示提取并显示路径中的目录名和文件名。
string path = @"c:\windows\System32\notepad.exe"; Console.WriteLine(Path.GetDirectoryName(path)); Console.WriteLine(Path.GetFileName(path));
其中,目录名为”C:\Windows\System32”,文件名为”notepad.exe”。
在.NET Framework中,文件和流是有区别的。文件是存储在磁盘上的数据集,它具备名称和相应的路径。当打开一个文件并对其进行读/写时,该文件就称为流(stream)。可是,流不只仅是指打开的磁盘文件,还能够是网络数据。.Net Framework容许在内存中建立流。此外,在控制台应用程序中,键盘输入和文本显示都是流。流包括如下基本操做:
* 读取(read):把数据从流传输到某种数据结构中,如输出到字符数组中。
* 写入(write):把数据从某种数据结构传输到流中,如把字节数组中的数据传输到流中。
* 定位(seek):在流中查找或从新定位当前位置。
(一)、操做流的类
1. Stream类
Stream类是全部流的抽象基类。Stream类的主要属性有CanRead(是否支持读取)、CanSeek(是否支持查找)、CanTimeout(是否能够超时)、CanWrite(是否支持写入)、Length(流的长度)、Position(获取或设置当前流中的位置)、ReadTimeout(获取或设置读取操做的超时时间)、WriteTimeout(获取或设置写操做的超时时间),主要方法有BeginRead(开始异步读操做),BeginWrite(开始异步写操做)、Close(关闭当前流)、EndRead(结束异步读操做)、EndWrite(结束异步写操做)、Flush(清除流的全部缓冲区并把缓冲数据写入基础设备)、Read(读取字节序列)、ReadByte(读取一个字节)、Seek(设置查找位置)、Write(写入字节序列)、WriteByte(写入一个字节)。
2. FileStream、MemoryStream和BufferedStream类
文件流类FileStream以流的形式读、写、打开、关闭文件。另外,它还能够用来操做诸如:管道、标准输入/输出等其余与文件相关的操做系统句柄。
内存流MemoryStream类用来在内存中建立流,以暂时保持数据,所以有了它就无须在硬盘上建立临时文件。它将数据封装为无符号的字节序列,能够直接进行读、写、查找操做。
缓冲流BufferedStream类表示把流先添加到缓冲区,再进行数据的读/写操做。缓冲区是存储区中用来缓存数据的字节块。使用缓冲区能够减小访问数据时对操做系统的调用次数,加强系统的读/写功能。
注意,FileStream类也有缓冲功能,在建立FileStream类的实例时,只须要指定缓冲区的大小便可。
3. StreamReader和StreamWriter类
流读取器StreamReader类用来以一种特定的编码(如:UTF-8)从字节流中读取字符,流写入器StreamWriter类用来以一种特定的编码(如:UTF-8)向流中写入字符。StreamReader和StreamWriter类通常用来操做文本文件。
4. BinaryReader和BinaryWriter类
BinaryReader类用特定的编码将基元数据类型读做二进制。BinaryWriter类以二进制形式将基元类型写入流,并支持用特定的编码写入字符串。
5. 文件流类 FileStream
文件流类FileStream公开了以文件为主的Stream,既支持同步读/写操做,也支持异步读/写操做,FileStream类的特色是操做字节和字节数组。这种方式不适合操做用字符数据构成的文本文件,适合处理非文本文件。FileStream类提供了对文件的低级而复杂的操做,所以可以实现更多高级的功能。
下面演示FileStreamWriter类的基本用法:
//要写入文件的字符数组 char[] m_cDataWrite = new char[100]; //包含要写入该流的数据的缓冲区 byte[] m_bDataWrite = new byte[100]; try { //建立d:\file.txt的FileStream对象 FileStream m_FileStream = new FileStream(@"d:\file.txt", FileMode.OpenOrCreate); //将要写入的字符串转换成字符数组 m_cDataWrite = "test filestream".ToCharArray(); //经过UTF-8编码方法将字符数组转成字节数组 Encoder m_Enc = Encoding.UTF8.GetEncoder(); m_Enc.GetBytes(m_cDataWrite, 0, m_cDataWrite.Length, m_bDataWrite, 0, true); //设置流当前位置为文件开始位置 m_FileStream.Seek(0, SeekOrigin.Begin); //将字节数组中的内容写入文件 m_FileStream.Write(m_bDataWrite, 0, m_bDataWrite.Length); if (m_FileStream != null) { //清除此流的缓冲区,使得全部缓冲的数据都写入到文件中 m_FileStream.Flush(); m_FileStream.Close(); } } catch (Exception ex) { Console.WriteLine("There is an IOException"); Console.WriteLine(ex.Message); } Console.WriteLine("Write to File Succeed!");
代码中首先给出了文件夹的路径,利用Write方法向文件中写入部分字符串。
下面演示FileStreamReader类的基本用法:
//要写入文件的字符数组 char[] m_cDataWrite = new char[100]; //包含要写入该流的数据的缓冲区 byte[] m_bDataWrite = new byte[100]; try { //建立d:\file.txt的FileStream对象 FileStream m_FileStream = new FileStream(@"d:\file.txt", FileMode.Open); //设置流当前位置为文件开始位置 m_FileStream.Seek(0, SeekOrigin.Begin); //将文件的内容存到字节数组中(缓存) m_FileStream.Read(m_bDataWrite, 0, 100); } catch (Exception ex) { Console.WriteLine("There is an IOException"); Console.WriteLine(ex.Message); } //经过UTF-8编码方法将字符数组转换成字符数组 Decoder m_Dec = Encoding.UTF8.GetDecoder(); m_Dec.GetChars(m_bDataWrite, 0, m_bDataWrite.Length, m_cDataWrite, 0); Console.WriteLine("Read from file Succeed!"); Console.WriteLine(m_cDataWrite);
代码中首先给出了文件夹的路径,利用Read方法从文件中读取了部分字符串。
6. StreamWriter和StreamReader类
应用FileStream类须要许多额外的数据类型转换操做,十分影响效率。StreamWriter类容许直接将字符和字符串写入文件。下面演示其用法:
try { //保留文件现有数据,以追加写入的方式打开d:\file.txt文件 StreamWriter m_SW = new StreamWriter(@"d:\file.txt", true); //向文件写入新字符串,并关闭StreamWriter m_SW.WriteLine("Another File Operation Method"); m_SW.Close(); } catch (Exception ex) { Console.WriteLine("There is an IOException"); Console.WriteLine(ex.Message); } StreamWriter类提供了另外一种从文件中读取数据的方法,下面演示其用法: try { //以绝对路径方式构造新的StreamReader对象 StreamReader m_SR = new StreamReader(@"d:\file.txt"); //用ReadToEnd方法将d:\file.txt中的数据所有读入到字符串m_Data中,并关闭StreamReader string m_Data = m_SR.ReadToEnd(); m_SR.Close(); Console.WriteLine(m_Data); } catch (Exception ex) { Console.WriteLine("There is an IOException"); Console.WriteLine(ex.Message); }
7. BinaryReader和BinaryWriter类
BinaryWriter类是除了FileStream和StreamWriter类以外另外一种向文件写入数据的方式,与以前两种方式不一样,BinaryWriter类将基础数据(如:字符串)以二进制形式写入文件流中,并支持用特定的编码写入。下面演示其用法:
FileStream m_FS = new FileStream(@"d:\data.dat", FileMode.Create); //经过文件流建立相应的BinaryWriter BinaryWriter m_BW = new BinaryWriter(m_FS); for(int i = 0; i < 11; i++) { //向d:\data.dat中写入数据 m_BW.Write((int)i); } m_BW.Close(); m_FS.Close();
代码中首先给出了文件夹的路径,利用BinaryWriter类的Write方法向文件中写入部分二进制字符。该文件是以二进制存储的,所以用记事本打开时,将没法观察到正确的字符,必须使用支持二进制的文本阅读器。
BinaryReader类是和BinaryWriter类相对应的二进制数据读取类。它用特定的编码将基元数据类型(如:字符串类型)读做二进制值。
FileStream m_FS = new FileStream(@"d:\data.dat", FileMode.Open, FileAccess.Read); //经过文件流建立相应的BinaryReader BinaryReader m_BR = new BinaryReader(m_FS); //从d:\data.dat中读取数据 for(int i = 0; i < 11; i++) { Console.WriteLine(m_BR.ReadInt32()); } m_BR.Close(); m_FS.Close(); Console.ReadLine();
代码中首先给出了文件夹的路径。利用BinaryReader类的ReadInt32方法从文件中读取了全部的二进制字符,并将其读为整数,便于输出。
5、 综合应用
(一)、建立日志文件
日志文件的做用是记录程序运行事件。一般使用文本文件保存数据。日志文件须要程序自动建立,并在指定的事件发生时,使用特定的格式把事件的相关数据记录到日志文件中。
一、技术要点
* 建立FileStream类实例时,可以经过该类构造函数的参数,指定打开文件的方式和读/写访问的方式。经过指定打开方式,实现日志文件的自动建立。
* 使用StreamWriter类实例写入文件时,由于部分数据可能因为系统缓慢而未能及时写入,因此在全部的写入操做完成后,须要调用Flush方法将缓冲区的文件内容更新到日志文件中。
* 使用StreamWriter类实例写入文件时,写入的方式与Console相似,可使用WriteLine向文件中写入一行文本数据。
2 、实现代码
const string _FILENAME = @"..\..\logfile.txt"; static void Main() { //从指定的目录以打开或者建立的形式读取日志文件 using (FileStream fs = new FileStream(_FILENAME, FileMode.OpenOrCreate, FileAccess.Write)) { //建立日志文件的写入流 StreamWriter sw = new StreamWriter(fs); //向日志文件写入日志信息 Log("日志文件建立成功", sw); //关闭日志文件写入流 sw.Close(); Console.WriteLine("日志文件已建立"); } //读取并显示日志文件 using (StreamReader sr = new StreamReader(_FILENAME, Encoding.UTF8)) { string strContent = sr.ReadToEnd(); sr.Close(); Console.WriteLine(strContent); } Console.ReadLine(); } static void Log(String message, TextWriter tw) { tw.Write("Log Entry:"); tw.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString()); tw.WriteLine(" :"); tw.WriteLine(" :{0}", message); tw.WriteLine("----------------------------------"); //将缓冲区中的内容更新到日志文件中 tw.Flush(); }
三、 源程序解读
(1)程序引用了System.IO命名空间,在程序头部应添加对该命名空间的引用。
(2)程序中定义了表示文件路径的常量_FILENAME
(3)在建立FileStream类实例时,使用FileMode.OpenOrCreate模式,即文件不存在时就建立,存在时就打开已存在的文件。
(二)、对日志文件的读/写操做
日志文件的读/写和文本文件的读/写方法基本相同,日志文件除了使用StreamReader类和StreamWriter类的实例进行读/写外,还有一些记录事件的要求。例如,在写入数据时使用追加的方式、控制日志文件的大小等。
一、 技术要点
* 使用FileInfo类实例获取日志文件的大小,实现当日志文件的大小超出指定范围时清空日志数据的功能。并使用该类实例的OpenWrite方法,建立FileStream类实例进行写入文件的操做,实现日志文件的自动建立功能。
* 使用StreamWriter类中定义的Seek方法,将写入位置移动到文件末尾,实现将数据以追加方式写入日志文件的功能。
* 使用StreamReader类中定义的Peek方法,判断读取器是否已经读到日志文件的末尾。
二、 实现代码
//表示日志文件路径及文件名称的字符串 const string FILENAME = @"..\..\logfile.txt"; static void Main(string[] args) { //写入日志信息 WriteLogFile(FILENAME, "日志信息一"); //读取日志文件 Console.WriteLine(ReadLogFile(FILENAME)); Console.ReadLine(); } static string ReadLogFile(string FileNameWithPath) { //从指定的目录以打开或建立的形式读取日志文件 FileStream fs = new FileStream(FileNameWithPath, FileMode.OpenOrCreate, FileAccess.Read); //定义输出字符串 StringBuilder output = new StringBuilder(); //初始化该字符串的长度为0 output.Length = 0; //为上面建立的文件流建立读取数据流 StreamReader read = new StreamReader(fs); //设置当前流的起始位置为文件流的起始点 read.BaseStream.Seek(0, SeekOrigin.Begin); //读取文件 while(read.Peek() > -1) { //取文件的一行内容并换行 output.Append(read.ReadLine() + "\n"); } //关闭释放读数据流 read.Close(); //返回读到的日志文件内容 return output.ToString(); } static void WriteLogFile(string FileNameWithPath, string Message) { //定义文件信息对象 FileInfo finfo = new FileInfo(FileNameWithPath); //判断文件是否存在以及是否大于2K if(finfo.Exists && finfo.Length > 2048) { //删除该文件 finfo.Delete(); } //建立只写文件流 using(FileStream fs = finfo.OpenWrite()) { //根据上面建立的文件流建立写数据流 StreamWriter w = new StreamWriter(fs); //设置写数据流的起始位置为文件流的末尾 w.BaseStream.Seek(0, SeekOrigin.End); //写入"Log Entry:" w.Write("Log Entry:"); //写入系统的当前时间并换行 w.Write("{0} {1} \r\n", DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString()); //写入日志内容并换行 w.Write(Message + "\r\n"); //写入-------------------------并换行 w.Write("----------------------\r\n"); //清空缓冲区内容,并把缓冲区内容写入基础流 w.Flush(); w.Close(); }
运行结果以下:
Log Entry:14:26:09 2017年5月1日
日志信息一
3 、源程序解读
(1)本示例程序使用写日志文件的WriteLogFile方法向文件中写入一条信息数据,再经过读取日志文件的ReadLogFile方法将日志文件的数据显示出来。本示例程序的流程图以下所示: 图1 对日志文件读/写操做示例程序流程图
(2)在写入日志文件的WriteLogFile方法中,首先打开并判断日志文件的大小是否超出了指定的尺寸。若是超出了指定的尺寸,就先将日志文件删除。而后经过FileInfo类实例的OpenWrite方法建立只写文件流,向该流中写入日志数据。
(3)在读取日志文件的ReadLogFile方法中,首先建立一个StringBuilder类的实例,用来获取日志文件中的文本数据。接着使用StreamReader类定义的BaseStream属性中的Seek方法,将读取器位置定位在流的开始位置,而后循环读取日志文件中的文本数据,并追加到StringBuilder类实例中,读取过程当中,经过StreamReader类中定义的Peek方法判断是否读到文件末尾。
(三)、复制文件
静态File类中提供了许多操做文件的方法,使用Copy方法复制文件是比较常见的一种操做,调用Copy方法时,可使用overwrite参数指定是否覆盖文件。
一、 技术要点
* 使用静态类File的Exists方法判断文件是否存在。
* 使用静态类File的Copy方法实现复制文件的功能,当文件存在时,经过指定override参数覆盖原有文件。
* 复制文件是系统操做,为了保证程序的稳定性,在复制文件的过程当中须要捕获并处理异常。
二、 实现代码
//源文件路径及文件名 const string SOURCEFILENAME = @"..\..\myfile.txt"; //目标文件路径及文件名 const string DESTINATIONFILENAME = @"..\..\result.txt"; static void Main(string[] args) { try { //判断源文件是否存在 if(!File.Exists(SOURCEFILENAME)) { Console.WriteLine("找不到源文件"); } else if (File.Exists(DESTINATIONFILENAME)) { Console.Write("目标文件已经存在,是否覆盖?(Y/N)"); if(Console.ReadKey(false).Key == ConsoleKey.Y) { //覆盖文件 File.Copy(SOURCEFILENAME, DESTINATIONFILENAME, true); Console.WriteLine("复制文件完成"); } else { Console.WriteLine("取消复制文件"); } } else { //直接复制 File.Copy(SOURCEFILENAME, DESTINATIONFILENAME); Console.WriteLine("复制文件完成"); } } catch (Exception) { Console.WriteLine("复制文件失败"); } Console.ReadLine(); }
3 、源程序解读
(1)本示例使用File静态类的方法实现文件的复制操做。首先判断源文件是否存在,若是源文件不存在,不做任何处理就返回。接着判断目标文件是否存在,若是目标文件不存在,就直接复制文件,不然就询问是否覆盖现有的目标文件,当用户选择覆盖时,使用源文件覆盖目标文件。
(2)在复制文件的方法调用时,将复制文件的代码放在一个try...catch结构中,以便捕获并处理复制文件时出现的异常。
(3)程序执行后,将程序文件所在目录下生成一个名为”result.txt”的文本文件。内容与”myfile.txt”文件一致。