C#文件操作基础(未完)

C#文件操作基础

    在.NET Framework中进行的所有的输入和输出工作都要使用到流。流是串行化设备的抽象串行化设备可以以线性方式存储数据,并可以以同样的方式访问:一次访问—个字节。此设备可以是磁盘文件、打印机、内存位置和或任何其他支持以线性方式读写的对象。
    当向某些外部目标写数据时,就要用到输出流,这可以是物理磁盘文件、网络位置、打印机或其他程序。
File与FileInof
System.IO包含另一个类File,它的功能与FileInfo一样,不过不同的是,File类成员为静态。所以,使用File代替FileInfo就不必实例化一个新FileInfo对象。 那么为什么有时还使用FileInfo呢?因为每次通过File类调用某个方法时,都要占用一定的cpu处理时间来进行安全检查,即使使用不同的File类的方法重复访问同一个文件时也是如此。而,FileInfo类只在创建FileInfo对象时执行一次安全检查。
常用的类:
Path------ 实用类,用于处理路径名称。
File(静态)------实用类,提供许多静态方法,用于移动、删除、和复制文件。
FileInfo------表示磁盘上的物理文件,具有可以处理此文件的方法,要完成对文件的读写工作,就必须创建Stream对像。
Directory(静态)------实用类,提供许多静态方法,用于移动、删除和复制目录。
DirectoryInfo------表示磁盘上的物理目录,具有可以处理此目录的方法

FileStream-------表示可以被写或被读,或二者都可的文件,此文件可以同步或异步读和写

StreamReader------从流中读取字符数据,并可通过使用FileStream被创建为基类。

StreamWriter------向流写字符数据,可通过使用FileStream被创建为基类。

FileSystemWatcher---- FileSystemWatcher是用于监控文件和目录,并在这些位置发生变化时,给出应用程序可以捕获的事件。



◆File和Directory类
作为实用类,File和Directory类都提供了许多方法,用于处理文件系统以及其中的文件和目录。这些是静态方法,涉及移动文件、查询和更新属性并创建FileStream对象。
File类一些最常用的静态方法:
1.File.Exist () 判断文件是否存在 参数 1
   该方法声明如下:public static bool Exists(string path); 
   下面的代码判断是否存在c:\tempuploads\newFile.txt文件。若存在,先复制该文件,然后其删除,最后将复制的文件移动;
若不存在,则先创建该文件,然后打开该文件并进行写入操作,最后将文件属性 设为只读、隐藏。 
if(File.Exists(@"c:\tempuploads\newFile.txt")) //判断文件是否存在
{
 CopyFile(); //复制文件
  DeleteFile(); //删除文件
  MoveFile(); //移动文件
}
else
{
  MakeFile(); //生成文件
  OpenFile(); //打开文件
  SetFile(); //设置文件属性

2.File.Create()------在规定的路径上创建文件 参数1
该方法的声明如下: public static FileStream Create(string path) 
   下面的代码演示如何在c:\tempuploads下创建名为newFile.txt的文件。 
   由于File.Create方法默认向所有用户授予对新文件的完全读/写访问权限,所以文件是用读/写访问权限打开的,必须关闭后才能由其他应用程序打开。
为此,所以需要使用FileStream类的Close方法将所创建的文件关闭。 
private void MakeFile()
{  
    FileStream NewText=File.Create(@"c:\tempuploads\newFile.txt"); 
  NewText.Close(); 
} 
3.File.Delete()------删除文件(不进回收站) 参数 1
该方法声明如下:public static void Delete(string path); 
  下面的代码演示如何删除c:\tempuploads目录下的newFile.txt文件。 
private void DeleteFile()
{
 File.Delete(@"c:\tempuploads\newFile.txt");

4.File.Open()文件打开方法---- 参数 2
---在规定的路径上返回FileStream对像
该方法的声明如下: 
public static FileStream Open(string path,FileMode mode) 
   下面的代码打开存放在c:\tempuploads目录下名称为newFile.txt文件,并在该文件中写入hello。 
private void OpenFile()

 FileStream TextFile=File.Open(@"c:\tempuploads\newFile.txt",FileMode.Append);
  byte [] Info = {(byte)'h',(byte)'e',(byte)'l',(byte)'l',(byte)'o'};
  TextFile.Write(Info,0,Info.Length);
  TextFile.Close();

5.File.Move()------将规定的文件移动到新位置,可以在新位置给文件规定不同的名字
参数 2
该方法声明如下:public static void Move(string sourceFileName,string destFileName); 
   下面的代码可以将c:\tempuploads下的BackUp.txt文件移动到c盘根目录下。
参数1:目标文件名;
参数2:移动后的文件名;
   注意: 
   1.只能在同一个逻辑盘下进行文件转移。如果试图将c盘下的文件转移到d盘,将发生错误。 
2.若第二个参数文件名已存在这出错。
private void MoveFile()
{
  File.Move(@"c:\tempuploads\BackUp.txt",@"c:\BackUp.txt");

6.File.SetAttributes() 属性设置 参数 2
   该方法声明如下: public static void SetAttributes(string path,FileAttributes fileAttributes); 
   下面的代码可以设置文件c:\tempuploads\newFile.txt的属性为只读、隐藏。 
private void SetFile()
{
 File.SetAttributes(@"c:\tempuploads\newFile.txt",FileAttributes.ReadOnly|FileAttributes.Hidden);

   文件除了常用的只读和隐藏属性外,还有Archive(文件存档状态),System(系统文件),Temporary(临时文件)等。关于文件属性的详细情况请参看MSDN中FileAttributes的描述。 
7.File.Copy()------将文件复制到规定的位置 参数 3
该方法声明如下:public static void Copy(string sourceFileName,string destFileName,bool overwrite); 
第一个参数:指定要移动的文件名;
第二个参数:设定移动后的新文件名;
第三个产生:如果指定的位置存在设定的文件名,参数为ture时覆盖,否则异常;
   下面的代码将c:\tempuploads\newFile.txt复制到c:\tempuploads\BackUp.txt。
   由于Cope方法的OverWrite参数设为true,所以如果BackUp.txt文件已存在的话,将会被复制过去的文件所覆盖。(如果文件存在着覆盖)
private void CopyFile()
{
  File.Copy(@"c:\tempuploads\newFile.txt",@"c:\tempuploads\BackUp.txt",true);

===============================================================================================================
  此外,File类对于Text文本提供了更多的支持。 
  · AppendText:将文本追加到现有文件 
  · CreateText:为写入文本创建或打开新文件 
  · OpenText:打开现有文本文件以进行读取 
  但上述方法主要对UTF-8的编码文本进行操作,从而显得不够灵活。在这里推荐读者使用下面的代码对txt文件进行操作。 
  · 对txt文件进行“读”操作,示例代码如下: 
StreamReader TxtReader = new StreamReader(@"c:\tempuploads\newFile.txt",System.Text.Encoding.Default);
string FileContent;
FileContent = TxtReader.ReadEnd();
TxtReader.Close(); 
  · 对txt文件进行“写”操作,示例代码如下: 
StreamWriter = new StreamWrite(@"c:\tempuploads\newFile.txt",System.Text.Encoding.Default);
string FileContent;
TxtWriter.Write(FileContent);
TxtWriter.Close(); 
========================================================================================
Directory类和DirectoryInfo类的一些常用的静态方法
包括文件夹的(创建,删除,属性,遍历,移动,是否存在)
[diˈrektəri]计算机程序目录 INFO 返回有关当前操作环境的信息
[əˈtribju:tz] 
DirectoryInfo.Attributes————————目录属性设置属性
   下面的代码设置c:\tempuploads\NewDirectory目录为只读、隐藏。与文件属性相同,目录属性也是使用FileAttributes来进行设置的。
该方法声明如下:DirectoryInfo.Attributes=FileAttributes.ReadOnly\FileAttributes.Hidden
private void SetDirectory()

  DirectoryInfo NewDirInfo = new DirectoryInfo(@"c:\tempuploads\NewDirectoty");
  NewDirInfo.Attributes = FileAttributes.ReadOnly|FileAttributes.Hidden;

Directory.CreateDirectory()------目录创建方法
  该方法声明如下:public static DirectoryInfo CreateDirectory(string path); 
   下面的代码演示在c:\tempuploads文件夹下创建名为NewDirectory的目录。 
private void MakeDirectory()
{
  Directory.CreateDirectory(@"c:\tempuploads\NewDirectoty"); 
}

Directory.Delete()------目录删除法

   该方法声明如下: public static void Delete(string path,bool recursive); 
   下面的代码可以将c:\tempuploads\BackUp目录删除。Delete方法的第二个参数为bool类型,它可以决定是否删除非空目录。
如果该参数值为true,将删除整个目录,即使该目录下有文件或子目录;若为false,则仅当目录为空时才可删除。 
private void DeleteDirectory()
{
  Directory.Delete(@"c:\tempuploads\BackUp",true);

Directory.GetDirectories()------获取当前目录子目录法
   该方法声明如下: public static string[] GetDirectories(string path); 
  下面的代码读出c:\tempuploads\目录下的所有子目录,并将其存储到字符串数组中。 
private void GetDirectory()
{
  string [] Directorys;
  Directorys = Directory. GetDirectories (@"c:\tempuploads");

Directory.GetFiles()-------获取当前目录下所以文件法
   该方法声明如下:public static string[] GetFiles(string path); 
   下面的代码读出c:\tempuploads\目录下的所有文件,并将其存储到字符串数组中。 
private void GetFile()
{
  string [] Files;
  Files = Directory. GetFiles (@"c:\tempuploads",);

Directory.Move()------目录移动方
  该方法声明如下:public static void Move(string sourceDirName,string destDirName); 
   下面的代码将目录c:\tempuploads\NewDirectory移动到c:\tempuploads\BackUp。 
private void MoveDirectory()
{
  File.Move(@"c:\tempuploads\NewDirectory",@"c:\tempuploads\BackUp");

Directory.Exists[ig'zist] ()——— 判断目录是否存在方法:
   该方法声明如下: public static bool Exists(string path); 
 下面的代码判断是否存在c:\tempuploads\NewDirectory目录。若存在,先获取该目录下的子目录和文件,然后其移动,最后将移动后的目录删除。
若不存在,则先创建该目录,然后将目录属性设为只读、隐藏
if(Directory.Exists(@"c:\tempuploads\NewDirectory")) //判断目录是否存在
{
  GetDirectory(); //获取子目录
  GetFile(); //获取文件
  MoveDirectory(); //移动目录
  DeleteDirectory(); //删除目录
}
else
{
  MakeDirectory(); //生成目录
  SetDirectory(); //设置目录属性

==================================================================================================================================
  注意: 
  路径有3种方式,当前目录下的相对路径、当前工作盘的相对路径、绝对路径。
以C:\Tmp\Book为例(假定当前工作目录为C:\Tmp)。“Book”,“\Tmp\Book”,“C:\Tmp\Book”都表示C:\Tmp\Book。 
  另外,在C#中 “\”是特殊字符,要表示它的话需要使用“\\”。由于这种写法不方便,C#语言提供了@对其简化。只要在字符串前加上@即可直接使用“\”。所以上面的路径在C#中应该表示为“Book”,@“\Tmp\Book”,@“C:\Tmp\Book”。 
==================================================================================================================================



◆FileInfo 类
FileInfo类不像File类,它不是静态的,没有静态方法,仅可用于实例化的对象。FileInfo对象表示磁盘或网络位置上的文件。提供文件的路径,就可以创建一个FileInfo对象:
FileInfo aFile = new FileInfo(@"C:/Log.txt");
注意:
本章处理的是表示文件路径的字符串,该字符串中有许多“\”字符,所以上述字符串的前缀@表示,这个字符串应逐个字符地解释,“\”解释为“\”,而不解释为转义字符。如果没有@前缀,就需要使用“\\”代替“\”,以避免把这个字符解释为转义字符。本章总是在字符串前面加上@。
也可以把目录名传送给FileInfo的构造函数,但实际上这并不是很有效。这么做会用所有的目录信息初始化FileInfo的基类FilesSystemInfo,但FileInfo中与文件相关的专用方法或属性不会工作。
FileInfo类提供的许多方法类似于File类的方法,但是因为File是静态类,它需要一个字符串参数为每个方法调用指定文件位置。因此,下面的调用可以完成相同的工作:
FileInfo aFile = new FileInfo("Data.txt");
if (aFile.Exists)
Console.WriteLine("File Exists");
if (File.Exists("Data.txt"))
Console.WriteLine("File Exists");
这段代码检查文件Data.txt是否存在。注意这里没有指定任何目录信息,这说明只检查当前的工作目录。这个目录包含调用此代码的应用程序。本章后面的“路径名和相对路径”一节会详细介绍。
大多数FileInfo方法以这种方式反映File方法。在大多数情况下使用什么技术并不重要,但下面的规则有助于确定哪个技术更合适:
●如果仅进行单一方法调用,则可以使用静态File类上的方法。在此,单一调用要快一些,因为.NET Framework不必实例化新对象,再调用方法。
●如果应用程序在文件上执行几种操作,则实例化FileInfo对象并使用其方法就更好一些。这会节省时间,因为对象已在文件系统上引用正确的文件,而静态类必须每次都寻找文件。
FileInfo类也提供了与底层文件相关的属性,其中一些属性可以用来更新文件,这些属性都继承于FilesSystemInfo,所以可应用于File和Directory类。FilesSystemInfo类的属性如表22-3所示

FileInfo专用的属性如表22-4所示。


注意,FileInfo对象本身不表示流。要读写文件,必须创建Stream对象。FileInfo对象为读写文件提供了几个返回实例化Stream对象的方法。
◆FileStream对象
FileStream对象表示在磁盘或网络路径上指向文件的流。当类提供向文件读写字节的方法时,经常使用StreamReader或StreamWriter执行这些功能。这是因为FileStream类操作字节和字节数组,而Stream类操作字符数据。字符数据易于使用,但是有些操作比如随机文件访问,就必须由FileStream对象执行。还有几种方法可以创建FileStream对象。构造函数具有许多不同的重载版本,最简单的构造函数仅仅带有两个参数,即文件名和FileMode枚举值。
一、
FileStream aFile = new FileStream(filename, FileMode.Member);
FileMode指定创建文件流实例时,打开指定文件时的模式,有如下几种可选值。

如:FileStream aFile = new FileStream(“Log.txt”,FileMode.OpenOrCreate);
二、
FileStream aFile = new FileStream(filename, FileMode.Member, FileAccess. Member);
FileAccess枚举成员,对文件进行不是FileAccess枚举成员指定的操作会导致抛出异常。此属性的作用是,基于用户的身份验证级别改变用户对文件的访问权限。

成 员 说 明
Read 打开文件,用于只读
Write 打开文件,用于只写
ReadWrite 打开文件,用于读写(默认)
读取文件的位置用seek:public long Seek(long offset,SeekOrigin origin)
参数一 Long offset是规定文件指针以字节为单位的移动距离;
参数二 SeekOrigin origin是规定开始计算的起始位置,此枚举包含3个值:Begin,Current和End。
 例:aFile.Seek(8,SeekOrigin.Begin);// SeekOrigin.Begin指得是文件指针从文件的第一个字节起;而参数'8'指得是移动到文件的第8个字节
例2:afile.Seek(2,SeekOrigin.Current)//在当前位置开始,再移动2个字节。
例3:aFile.Seek(-5,SeekOrigin.End)//在文件末端位置开始,倒数5个字节。
读取数据
使用FileStream类读取数据不像使用StreamReader和StreamWriter类读取数据那么容易,这是因为FileStream类只能处理原始字节(raw byey),这使得FileStream类可以用于任何数据文件,而不仅仅是文本文件,通过读取字节数据就可以读取类似图像和声音的文件。这种灵活性的代价是不能使用它直接读入字符串,而使用StreamWriter和StreaMeader类却可以这样处理,从是有几种转换类可以很容易地将字节数组转换为字符数组,或者进行相反的操作。

Read()方法是从FileStream对象所指向的文件访问数据的主要手段:
public int Read(byte[] array,int offset, int count)
第一个参数byte[] array是被传输进来的字节数组,用以接受FileStream对象中的数据。
第二个参数int offset是字节数组中开始写入数据的位置,它通常是0。
第三个参数int count是规定从文件中读出多少字节。
写入数据
写入数据的流程是先获取字节数组,再把字节数据转换为字符数组,然后把这个字符数组用Write()方法写入到文件中,当然在写入的过程中,可以确定在文件的什么位置写入,写多少字符等等。
文件读写的范例:读取文件
(1) 在目录C:\BegVCSharp\Chapter22下创建一个新的控制台应用程序ReadFile。
(2) 在Program.cs文件的顶部添加下面的using指令: 
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
(3) 在Main()方法中添加下面的代码:
static void Main(string[] args)
{
byte[] byData = new byte[100];
char[] charData = new Char[100];
try
{
FileStream aFile = new FileStream("../../Program.cs",FileMode.Open);
aFile.Seek(135,SeekOrigin.Begin);
aFile.Read(byData,0,200);
}
catch(IOException e)
{
Console.WriteLine("An IO exception has been thrown!");
Console.WriteLine(e.ToString());
Console.ReadKey();
return;
}
Decoder d = Encoding.UTF8.GetDecoder();
d.GetChars(byData, 0, byData.Length, charData, 0);
Console.WriteLine(charData);
Console.ReadKey();
}
示例的说明
此应用程序打开自己的.cs文件,用于读取。它在下面的代码行中使用..字符串向上逐级导航两个目录,找到该文件:
FileStream aFile = new FileStream("../../Program.cs",FileMode.Open);
下面两行代码实现查找工作,并从文件的具体位置读取字节:
aFile.Seek(135,SeekOrigin.Begin);
aFile.Read(byData,0,200);
第一行代码将文件指针移动到文件的第135个字节。在Program.cs中,这是namespace的 “n”;其前面的135个字符是using指令和相关的#region。第二行将接下来的200个字节读入到byData字节数组中。
注意这两行代码封装在try…catch块中,以处理可能抛出的异常。
try
{
aFile.Seek(135,SeekOrigin.Begin);
aFile.Read(byData,0,100);
}
catch(IOException e)
{
Console.WriteLine("An IO exception has been thrown!");
Console.WriteLine(e.ToString());
Console.ReadKey();
return;
}
文件IO涉及到的所有操作都可以抛出类型为IOException的异常。所有产品代码都必须包含错误处理,尤其是处理文件系统时更是如此。本章的所有示例都具有错误处理的基本形式。
从文件中获取了字节数组后,就需要将其转换为字符数组,以便在控制台显示它。为此,使用System.Text命名空间的Decoder类。此类用于将原始字节转换为更有用的项,比如字符:
Decoder d = Encoding.UTF8.GetDecoder();
d.GetChars(byData, 0, byData.Length, charData, 0);//c# GetChars(Byte[], Int32, Int32, Char[], Int32)
这些代码基于UTF8编码模式创建了Decoder对象。这就是Unicode编码模式。然后调用GetChars()方法,此方法提取字节数组,将它转换为字符数组。完成之后,就可以将字符数组输出到控制台。
写入数据:
向随机访问文件中写入数据的过程与从中读取数据非常类似。首先需要创建一个字节数组;最简单的办法是首先构建要写入文件的字符数组。然后使用Encoder对象将其转换为字节数组,其用法非常类似于Decoder。最后调用Write()方法,将字节数组传送到文件中。
试试看:将数据写入随机访问文件
(1) 在C:\BegVCSharp\Chapter22目录下创建一个新的控制台应用程序WriteFile。
(2) 如上所示,在Program.cs文件顶部添加下面的using指令:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
(3) 在Main()方法中添加下面的代码:
static void Main(string[] args)
{
byte[] byData;
char[] charData;
try
{
FileStream aFile = new FileStream("Temp.txt", FileMode.Create);
charData = "My pink half of the drainpipe.".ToCharArray();
byData = new byte[charData.Length];
Encoder e = Encoding.UTF8.GetEncoder();
e.GetBytes(charData, 0, charData.Length, byData, 0, true);
aFile.Seek(0, SeekOrigin.Begin);
aFile.Write(byData, 0, byData.Length);
}
catch (IOException ex)
{
Console.WriteLine("An IO exception has been thrown!");
Console.WriteLine(ex.ToString());
Console.ReadKey();
return;
}
}
示例的说明
此应用程序在自己的目录中打开文件,并在文件中写入了一个简单的字符串。在结构上这个示例非常类似于前面的示例,只是用Write()代替了Read(),用Encoder代替了Decoder。

下面的代码行使用String类的ToCharArray()静态方法,创建了字符数组。因为C#中的所有事物都是对象,文本“My pink half of the drainpipe.”实际上是一个String对象,所以甚至可以在字符串上调用这些静态方法。

CharData = " My pink half of the drainpipe. ".ToCharArray();

下面的代码行显示了如何将字符数组转换为FileStream对象需要的正确字节数组。

Encoder e = Endoding.UTF8.GetEncoder();

e.GetBytes(charData,0,charData.Length, byData,0,true);

这次,要基于UTF8编码方法来创建Encoder对象。也可以将Unicode用于解码。这里在写入流之前,需要将字符数据编码为正确的字节格式。在GetBytes()方法中可以完成这些工作,它可以将字符数组转换为字节数组,并将字符数组作为
第一个参数(本例中的charData)待转义的字符数组;
第二个参数数组中起始位置的下标作(0表示数组的开头)。
第三个参数是要转换的字符数量(charData.Length,charData数组中的元素个数)。
第四个参数转义后写入的字节数组(byData),
第五个参数是在字节数组中开始写入位置的下标(0表示byData数组的开头)。
第六个参数决定在结束后Encoder对象是否应该更新其状态,即Encoder对象是否仍然保留它原来在字节数组中的内存位置。这有助于以后调用Encoder对象,但是当只进行单一调用时,这就没有什么意义。最后对Encoder的调用必须将此参数设置为true,以清空其内存,释放对象,用于垃圾回收。
之后,使用Write()方法向FileStream写入字节数组就非常简单:
aFile.Seek(0,SeekOrigin.Begin);
aFile.Write(byData,0,byData.Length);
与Read()方法一样,Write()方法也有三个参数:要写入的数组,开始写入的数组下标和要写入的字节数。
◆StreamWriter对像
操作字节数组比较麻烦,因为使用FileStream对象非常困难,那么,还有简单一些的方法吗?恐怕是没有了,因为有了FileStream对象,通常都会把它包装在StreamWriter或Stream Reader中,并使用它们的方法来处理文件。如果不需要将文件指针改变到任意位置,这些类就很容易操作文件。
StreamWriter类允许将字符和字符串写入到文件中,它处理底层的转换,向FileStream对象写入数据。

还有许多方法可以创建StreamWriter对象。如果已经有了FileStream对象,则可以使用此对象来创建StreamWriter对象:
FileStream aFile = new FileStream("Log.txt",FileMode.CreateNew);
StreamWriter sw = new StreamWriter(aFile);
也可以直接从文件中创建StreamWriter对象:
StreamWriter sw = new StreamWriter("Log.txt",true);
这个构造函数的参数是文件名和一个Boolean值:
● 如果此值设置为false,则创建一个新文件,或者截取现有文件并打开它。
● 如果此值设置为true,则打开文件,保留原来的数据。如果找不到文件,则创建一个新文件。
与创建FileStream对象不同,创建StreamWriter对象不会提供一组类似的选项:除了使用Boolean值添加到文件的末尾或创建新文件之外,根本没有像FileStream类那样指定FileMode属性的选项。而且,没有设置FileAccess属性的选项,因此总是有对文件的读/写权限。为了使用高级参数,必须先在FileStream构造函数中指定这些参数,然后在FileStream对象中创建StreamWriter,如下面的示例所示。
(1) 在C:\BegVCSharp\Chapter22目录下创建一个新的控制台应用程序StreamWrite。
(2) 我们将再次使用System.IO命名空间,因此在Program.cs文件顶部添加下面的using指令:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
(3) 在Main()方法中添加下面的代码:
static void Main(string[] args)
{
try
{
FileStream aFile = new FileStream("Log.txt", FileMode.OpenOrCreate);
StreamWriter sw = new StreamWriter(aFile);
bool truth = true;
sw.WriteLine("Hello to you.");
sw.WriteLine("It is now {0} and things arelookinggood.",DateTime.Now.ToLongDateString()); 
sw.Write("More than that,");
sw.Write(" it's {0} that C# is fun.", truth);
sw.Close();
}
catch(IOException e)
{
Console.WriteLine("An IO exception has been thrown!");
Console.WriteLine(e.ToString());
Console.ReadLine();
return;
}
}
(4) 编译并运行该项目。如果没有发现错误,则项目会很快运行,并关闭。因为我们在控制台上没有显示任何内容,所以在控制台中无法看到程序的执行情况。
(5) 进入应用程序目录,找到Log.txt文件,它位于StreamWrite\bin\Debug文件夹,这是因为我们使用了相对路径。

示例的说明
这个简单的应用程序演示了StreamWriter类的两个最重要的方法:Write()和WriteLine()。这两个方法具有许多重载的版本,可以完成更高级的文件输出,但是本示例只使用基本的字符串输出。

WriteLine()方法会写入传递给它的字符串,其后跟有换行符。在此示例中,下一个写入操作在新行上开始。

如同可以向控制台写入格式化数据一样,也可以向文件写入格式化数据。例如,可以使用标准格式化参数把变量的值写入文件:

sw.WriteLine("It is now {0} and things are looking good.",

DateTime.Now.ToLongDateString());

DateTime.Now存储了当前日期,ToLongDateString()方法用于把这个日期转换为易于读取的格式。

Write()方法只是把传送给它的字符串写入文件,但不追加换行符,因此可以使用多个Write()语句写入完整的句子或段落。

sw.Write("More than that,");

sw.Write(" it's {0} that C# is fun.", truth);

这里也使用了格式化参数,这次是使用Write()显示布尔值truth。前面把这个变量设置为true,其值会自动格式化,转换为字符串True。

可以使用Write()和格式化参数写入用逗号分隔的文件:

[StreamWriter object].Write("{0},{1},{2}", 100, "A nice product", 10.50);

在更复杂的示例中,这些数据还可以来自数据库或其他数据源。

◆StreamReader对象
输入流用于从外部源读取数据。在很多情况下,数据源是磁盘上的文件或网络的某些位置。任何可以发送数据的位置都可以是数据源,比如网络应用程序、Web服务,甚至是控制台。
用来从文件中读取数据的类是StreamReader。同StreamWriter一样,这是一个通用类,可以用于任何流。下面的示例会再次围绕FileStream对象构造StreamReader类,使它指向正确的文件。
StreamReader对象的创建方式非常类似于StreamWriter对象。创建它的最常见方式是使用前面创建的FileStream对象:
FileStream aFile = new FileStream("Log.txt",FileMode.Open);
StreamReader sr = new StreamReader(aFile);
同StreamWriter一样,StreamReader类可以直接在包含具体文件路径的字符串中创建:
StreamReader sr = new StreamReader("Log.txt");
试试看:流输入
(1) 在C:\BegVCSharp\Chapter22目录下创建一个新控制台应用程序StreamRead。
(2) 必须再次输入System.IO命名空间,因此将下面的代码放在Program.cs文件的顶部:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
(3) 在Main()方法中添加下面的代码:
static void Main(string[] args)
{
string strLine;
try
{
FileStream aFile = new FileStream("Log.txt", FileMode.Open);
StreamReader sr = new StreamReader(aFile);
strLine = sr.ReadLine();
while(strLine != null)
{
Console.WriteLine(strLine);
strLine = sr.ReadLine();
}
sr.Close();
}
catch(IOException e)
{
Console.WriteLine("An IO exception has been thrown!");
Console.WriteLine(e.ToString());
return;
}
Console.ReadKey();
}

(4) 把前面示例中创建的Log.txt文件复制到StreamRead\bin\Debug目录下。如果没有Log.txt文件, FileStream构造函数找不到该文件,就会抛出异常。

(5) 运行该应用程序,可以看到写入到控制台的文件文本
示例的说明
这个应用程序非常类似于前面的应用程序。其明显的区别就是,它是在读取数据,而不是写入数据。同前面一样,必须导入System.IO命名空间,才能访问需要的类。
使用ReadLine()方法从文件中读取文本。这个方法读取回车符之前的文本,并以字符串的形式返回结果文本。当到达文件尾时,该方法就返回空值,通过这种方法可以测试文件是否已到达了尾部。注意使用while循环时,在执行循环体的代码之前进行检查,确保读取的行不为空,这样就只显示文件的有效内容:
strLine = sr.ReadLine();
while(strLine != null)
{
Console.WriteLine(strLine);
strLine = sr.ReadLine();
}
1. 读取数据
ReadLine()方法不是在文件中访问数据的惟一方法。StreamReader类还有许多读取数据的方法。
读取数据最简单的方法是Read()。此方法将流的下一个字符作为正整数值返回,如果到达了流的结尾处,则返回–1。使用Convert实用类可以把这个值转换为字符。在上面的示例中,程序的主体可以按如下方式编写:
StreamReader sr = new StreamReader(aFile);
int nChar;
nChar = sr.Read();
while(nChar != -1)
{
Console.Write(Convert.ToChar(nChar));
nChar = sr.Read();
}
sr.Close();
对于小型文件,可以使用一个非常方便的方法,即ReadToEnd()方法。此方法读取整个文件,并将其作为字符串返回。在此,前面的应用程序可以简化为:
StreamReader sr = new StreamReader(aFile);
strLine = sr.ReadToEnd();
Console.WriteLine(strLine);
sr.Close();
这似乎非常容易和方便,但必须小心。将所有的数据读取到字符串对象中,会迫使文件中的数据放到内存中。应根据数据文件的大小禁止这样处理。如果数据文件非常大,最好将数据留在文件中,并使用StreamReader的方法访问文件。
————————————————————————————————————————————
◆分隔文件
用分隔符分隔的文件是数据存储的一种常见形式,由许多老系统使用,如果应用程序必须与这种系统协作,就会经常遇到用分隔符分隔的文件格式。最常见的分隔符是逗号,例如Excel电子表格、Access数据库或SQL Server数据库中的数据都可以导出为用逗号分隔的值(CSV)文件。
前面介绍了如何使用StreamWriter类编写使用这种方法存储数据的文件。使用逗号分隔的文件也易于读取。如第5章所述,String类的Split()方法可以用于将字符串转换为基于所提供的分隔符的数组。如果规定逗号为分隔符,它就会创建尺度合理的字符串数组,其中包含在初始逗号分隔的字符串中的全部数据。
下面的示例将说明其用途。这个示例处理用逗号分隔的值,把它们加载到List<Dictionary<string, string>>对象中。这个示例非常通用,如果需要处理用逗号分隔的值,就可以使用这个技术。
试试看:逗号分隔的值
(1) 在C:\BegVCSharp\Chapter22目录下创建一个新控制台应用程序CommaValues。
(2) 将下面的代码放在Program.cs文件的顶部,需要导入System.IO命名空间,才能进行文件处理:

using System;

using System.Collections.Generic;

using System.Text;

using System.IO;

(3) 在Program.cs主体的Main()方法之前,添加下面的GetData()方法:

private static List<Dictionary<string, string>> GetData(out List<string> columns)

{

string strLine;

string[] strArray;

char[] charArray = new char[] {','};

List<Dictionary<string, string>> data = new List<Dictionary<string, string>>();

columns = new List<string>();


try

{

FileStream aFile = new FileStream("../../../SomeData.txt", FileMode.Open);

StreamReader sr = new StreamReader(aFile);


// Obtain the columns from the first line.


// Split row of data into string array

strLine = sr.ReadLine();

strArray = strLine.Split(charArray);


for (int x = 0; x <= strArray.GetUpperBound(0); x++)

{

columns.Add(strArray[x]);

}


strLine = sr.ReadLine();

while (strLine != null)

{

// Split row of data into string array

strArray = strLine.Split(charArray);

Dictionary<string, string> dataRow = new Dictionary<string, string>();


for (int x = 0; x <= strArray.GetUpperBound(0); x++)

{

dataRow.Add(columns[x], strArray[x]);

}


data.Add(dataRow);


strLine = sr.ReadLine();

}


sr.Close();

return data;

}

catch (IOException ex)

{

Console.WriteLine("An IO exception has been thrown!");

Console.WriteLine(ex.ToString());

Console.ReadLine();

return data;

}

}

(4) 在Main()方法中添加下面的代码:

static void Main(string[] args)

{

List<string> columns;

List<Dictionary<string, string>> myData = GetData(out columns);


foreach (string column in columns)

{

Console.Write("{0,-20}", column);

}

Console.WriteLine();


foreach (Dictionary<string, string> row in myData)

{

foreach (string column in columns)

{

Console.Write("{0,-20}", row[column]);

}

Console.WriteLine();

}

Console.ReadKey();

}

(5) 在VS中,从File | New | File对话框中选择Text File,创建一个新文本文件。

(6) 在新文本文件中输入下面的文本:

ProductID,Name,Price

1,Spiky Pung,1000

2,Gloop Galloop Soup,25

4,Hat Sauce,12

(7) 在CommaValues项目目录下把文件存储为SomeData.txt。

(8) 运行该应用程序 —— 可以看到写入到控制台的文件文本,如图22-6所示。
示例的说明

同前面的示例相同,这个应用程序也逐行地把数据读入字符串。但是,因为这是一个包含用逗号分隔的文本值的文件,所以以不同的方式处理它。而且,还把读取的值存储在一个数据结构中。

首先需要查看一下用逗号分隔的数据:

ProductID,Name,Price

1,Spiky Pung,1000

注意第一行包含数据列的名称,其他行包含数据。所以,过程应从文件的第一行中获取列名,再从其他的行中获取数据。

下面看看GetData()方法,这个方法声明为static,所以可以在不创建类实例的情况下调用它。这个方法返回一个List<Dictionary<string, string>>对象,下面要创建这个对象,并使用逗号分隔的文本文件中的数据填充它。该方法还返回一个包含标题名的List<string>对象。下面的代码初始化了这些对象:

List<Dictionary<string, string>> data = new List<Dictionary<string, string>>();

columns = new List<string>();

columns包含逗号分隔的文本文件中第一行的列名,data包含其他行上的值。

首先创建一个FileStream对象,再构建一个StreamReader对象,就象前面的示例那样。现在就可以读取文件的第一行,从这个字符串中创建一个字符串数组:

strLine = sr.ReadLine();

strArray = strLine.Split(charArray);

第5章介绍了Split()方法,它的参数是一个字符数组,在这里该数组只包含“,”, 所以strArray包含的字符串数组是在strLine的每个“,”实例处进行分隔而构建的。当前是从文件的第一行中读取数据,这一行包含数据列的名称,所以需要迭代strArray中的每个字符串,把它们添加到columns中:

for (int x = 0; x <= strArray.GetUpperBound(0); x++)

{

columns.Add(strArray[x]);

}

数据有了列名称后,就可以读取数据了。其代码与前面的StreamRead示例相同,只是需要把Dictionary<string, string>对象添加到data中:

strLine = sr.ReadLine();

while (strLine != null)

{

// Split row of data into string array.

strArray = strLine.Split(charArray);

Dictionary<string, string> dataRow = new Dictionary<string, string>();


for (int x = 0; x <= strArray.GetUpperBound(0); x++)

{

dataRow.Add(columns[x], strArray[x]);

}


data.Add(dataRow);


strLine = sr.ReadLine();

}

对于文件中的每一行,都创建一个新的Dictionary<string, string>对象,用一行数据填充它。这个集合中的每一项都有一个对应于列名的键和一个值,这个值对应于该行的列值。键从前面创建的columns对象中提取,值从使用Split()获得的字符串中提取,Split()方法用于从数据文件中提取一行文本。

读取完文件中的所有数据,就关闭StreamReader,返回数据。

Main()方法中的代码把从GetData()方法获得的数据放在变量myData和columns中,并在控制台上显示这些信息。首先显示每一列的名称:

foreach (string column in columns)

{

Console.Write("{0,-20}", column);

}

Console.WriteLine();

格式化字符串{0,–20}中的–20部分确保显示的名称在20字符长的列中左对齐,这有助于格式化显示结果。

最后迭代myData集合中的每个Dictionary<string, string>对象,显示该行中的值,这里再次使用格式化字符串来格式化输出结果。

foreach (Dictionary<string, string> row in myData)

{

foreach (string column in columns)

{

Console.Write("{0,-20}", row[column]);

}

Console.WriteLine();

}

可以看到,使用.NET Framework从逗号分隔的值(CSV)文件中提取有意义的数据非常容易。这个技术也很容易与第24章介绍的数据访问技术合并使用,即CSV文件中的数据可以像其他数据源(如数据库)中的数据那样操作。但从CSV文件中提取的数据没有数据类型信息。当前是把所有的数据看作字符串,但对于企业级的商务应用程序来说,就需要更进一步,给提取的数据添加类型信息。这些附加信息可以存储在CSV文件中,可以手工操作,也可以从文件的字符串中推断,这取决于特定的应用程序。

下一章介绍的XML也是一个存储和传输数据的优秀方法,而CSV文件仍然非常普遍,还会使用相当长的时间。逗号分隔文件的优点是非常简洁,因此要比XML文件小些。
————————————————————————————————————————————

文件操作
向文件中追加文本 File.AppendText FileInfo.AppendText 
重命名或移动文件 File.Move FileInfo.MoveTo 
删除文件 File.Delete FileInfo.Delete 
复制文件 File.Copy FileInfo.CopyTo 
获取文件大小 FileInfo.Length 
获取文件属性 File.GetAttributes 
设置文件属性 File.SetAttributes 
确定文件是否存在 File.Exists 
读取二进制文件 对刚创建的数据文件进行读取和写入 
写入二进制文件 对刚创建的数据文件进行读取和写入 
检索文件扩展名 Path.GetExtension 
检索文件的完全限定路径 Path.GetFullPath 
检索路径中的文件名和扩展名 Path.GetFileName 
更改文件扩展名 Path.ChangeExtension 

目录操作

System.IO 类 

目录操作
string[] drives = Directory.GetLogicalDrives(); //本地驱动器的名,如:C:\等
string path = Directory.GetCurrentDirectory(); //获取应用程序的当前工作目录
Path.GetFileName(@"c:\dir\file.txt"); //获取子目录的名字,result的结果是file.txt
Directory.GetFiles(路径及文件名) //获取指定目录中的文件名(文件列表)
DirectoryInfo di = new DirectoryInfo(@"f:\MyDir"); //构造函数创建目录
DirectoryInfo di=Directory.CreateDirectory(@"f:\bbs"); //创建对象并创建目录
if (di.Exists == false) //检查是否存在此目录
di.Create(); //创建目录
DirectoryInfo dis = di.CreateSubdirectory("SubDir"); //以相对路径创建子目录
dis.Delete(true); //删除刚创建的子目录
di.Delete(true); //删除创建目录

文件操作
Directory.Delete(@"f:\bbs2", true); //删除目录及其子目录和内容(如为假不能删除有内容的目录包括子目录)
Directory.GetDirectories 方法 //获取指定目录中子目录的名称
string[] dirs = Directory.GetDirectories(@"f:\", "b*"); 
Console.WriteLine("此目录中以b开头的子目录共{0}个!", dirs.Length);
foreach (string dir in dirs) { Console.WriteLine(dir); }
Directory.GetFileSystemEntries //获取指定目录中的目录及文件名
Directory.GetLogicalDrives //检索此计算机上格式为“<驱动器号>:\”的逻辑驱动器的名称,【语法同上】
Directory.GetParent //用于检索父目录的路径。
DirectoryInfo a = Directory.GetParent(path);
Console.WriteLine(a.FullName);Directory.Move //移动目录及其在内的所有文件
Directory.Move(@"f:\bbs\1", @"f:\bbs\2"); //将文件夹1内的文件剪到文件夹2内 文件夹2是刚创建的



Stream // 对字节的读写操作(包含对异步操作的支持) Reading Writing Seeking

BinaryReader和BinaryWriter // 从字符串或原始数据到各种流之间的读写操作

FileStream类通过Seek()方法进行对文件的随机访问,默认为同步

TextReader和TextWriter //用于gb2312字符的输入和输出

StringReader和StringWriter //在字符串中读写字符

StreamReader和StreamWriter //在流中读写字符

BufferedStream 为诸如网络流的其它流添加缓冲的一种流类型.

MemoryStream 无缓冲的流

NetworkStream 互联网络上的流



编码转换
Encoding e1 = Encoding.Default; //取得本页默认代码 
Byte[] bytes = e1.GetBytes("中国人民解放军"); //转为二进制
string str = Encoding.GetEncoding("UTF-8").GetString(bytes); //转回UTF-8编码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值