文件夹和文件、Path类、流、序列化

循环访问目录树

  参考: http://msdn.microsoft.com/zh-cn/library/bb513869.aspx

     循环访问目录树”的意思是在指定的根文件夹下,访问每个嵌套子目录中任意深度的所有文件。不必打开每一个文件,只检索string 形式的文件名或子目录名,或者可以检索 System.IO.FileInfo  System.IO.DirectoryInfo 对象形式的其他信息

  如果确信拥有指定根目录下所有目录的访问权限,则您可以使用 System.IO.SearchOption.AllDirectories 标志。该标志返回与指定模式相匹配的所有嵌套子目录。

 public class RecursiveFileSearch
    {
        static System.Collections.Specialized.StringCollection log = new System.Collections.Specialized.StringCollection();

        static void Main()
        {
            // 从驱动端搜索整个电脑
            string[] drives = System.Environment.GetLogicalDrives();//从根目录下面开始

            foreach (string dr in drives)
            {
                System.IO.DriveInfo di = new System.IO.DriveInfo(drives[0]);

                //跳过不是只读文件  
                // 这个参数可变,变为需要的
                if (!di.IsReady)
                {
                    Console.WriteLine("The drive {0} could not be read", di.Name);
                    continue;
                }
                System.IO.DirectoryInfo rootDir = di.RootDirectory;
                WalkDirectoryTree(rootDir);
            }

            // 写出不能被处理的文件
            Console.WriteLine("Files with restricted access:");
            foreach (string s in log)
            {
                Console.WriteLine(s);
            }
            // 在调试模式下打开控制窗口
            Console.WriteLine("Press any key");
            Console.ReadKey();
        }

        static void WalkDirectoryTree(System.IO.DirectoryInfo root)
        {
            System.IO.FileInfo[] files = null;
            System.IO.DirectoryInfo[] subDirs = null;

            // 首先,处理所有在这个文件夹中的文件
            try
            {
                files = root.GetFiles("*.docx");
            }
            //  当一个文件需要访问权限比较大时,则会抛出。
            catch (UnauthorizedAccessException e)
            {
                //这段代码只写出消息并继续递归。你可以决定做些不同的事情。例如,你可以试着提高你的权限和访问文件。
                log.Add(e.Message);
            }

            catch (System.IO.DirectoryNotFoundException e)
            {
                Console.WriteLine(e.Message);
            }

            if (files != null)
            {
                foreach (System.IO.FileInfo fi in files)
                {
                    //在这个例子中,我们只访问现有FileInfo对象。
                    //如果我们想要开放、删除或修改文件,然后需要一个try - catch块来处理的文件已被删除从调用TraverseTree()。
                    Console.WriteLine(fi.FullName);
                }

                // 现在找到这个目录下所有的子目录。
                subDirs = root.GetDirectories();

                foreach (System.IO.DirectoryInfo dirInfo in subDirs)
                {
                    // Resursive呼吁每个子目录。
                    WalkDirectoryTree(dirInfo);
                }
            }
        }
    }
View Code

  首先注意这里面用了StringCollection集合,

  System.Collections.Specialized.StringCollection log = new System.Collections.Specialized.StringCollection();具体请参考:此处。

  找到集合后可以过滤要找的文件,用DirectoryInfo对象 root对象的GetFiles(“*.*”),此处可以过滤想要找的文件,返回类型是FileInfo,也就是说接下来就可以操作想要的对象了,例如:"*.txt"。

  递归遍历目录树:递归方法很简洁,但如果目录树很大且嵌套很深,则有可能会引起堆栈溢出异常。对于所处理的特定异常以及在每个文件和文件夹上执行的特定操作,都只是作为示例提供。 您应该修改此代码来满足自己特定的需要。

获取有关文件、文件夹和驱动的信息

  参考:http://msdn.microsoft.com/zh-cn/library/6yk7a1b0.aspx

  在 .NET Framework 中,可以使用以下类来访问文件系统信息:

  • System.IO.FileInfo
  • System.IO.DirectoryInfo
  • System.IO.DriveInfo
  • System.IO.Directory
  • System.IO.File

  File: 提供用于创建、复制、删除、移动和打开文件的静态方法,并协助创建 FileStream 对象。

  FileInfo:提供创建、复制、删除、移动和打开文件的属性和实例方法,并且帮助创建 FileStream 对象。 

  Directory:公开用于创建、移动和枚举通过目录和子目录的静态方法。 此类不能被继承。

  DirectoryInfo:公开用于创建、移动和枚举目录和子目录的实例方法。

  DriveInfo:提供对有关驱动器的信息的访问。

  System.IO.Directory  System.IO.File 类提供用于检索有关目录和文件的信息的静态方法。只说一点,静态类的方法可以共享。  

  class FileSysInfo
    {
        static void Main()
        {
            System.IO.DriveInfo di = new System.IO.DriveInfo(@"C:\");
            Console.WriteLine(di.TotalFreeSpace);
            Console.WriteLine(di.VolumeLabel);

            // 得到根目录,打印出相关信息
            System.IO.DirectoryInfo dirInfo = di.RootDirectory;
            Console.WriteLine(dirInfo.Attributes.ToString());
            // 目录中的文件盒打印一些信息
            System.IO.FileInfo[] fileNames = dirInfo.GetFiles("*.*");

            foreach (System.IO.FileInfo fi in fileNames)
            {
                Console.WriteLine("{0}: {1}: {2}", fi.Name, fi.LastAccessTime, fi.Length);
            }

            //得到的直接子目录下的根。
            System.IO.DirectoryInfo[] dirInfos = dirInfo.GetDirectories("*.*");

            foreach (System.IO.DirectoryInfo d in dirInfos)
            {
                Console.WriteLine(d.Name);
            }

            //目录和文件类提供一些静态方法来访问文件和目录。
            // 获取当前应用程序目录。
            string currentDirName = System.IO.Directory.GetCurrentDirectory();
            Console.WriteLine(currentDirName);

            //是一个文件名作为字符串数组而不是FileInfo对象。
            //使用这种方法,当存储空间是一个问题,当你尝试访问该文件,可能会用文件名引用一段时间。
            string[] files = System.IO.Directory.GetFiles(currentDirName, "*.txt");

            foreach (string s in files)
            {
                // 只在需要时创建FileInfo对象,以确保尽可能是当前的信息。       
                System.IO.FileInfo fi = null;
                try
                {
                    fi = new System.IO.FileInfo(s);
                }
                catch (System.IO.FileNotFoundException e)
                {
                    Console.WriteLine(e.Message);
                    continue;
                }
                Console.WriteLine("{0} : {1}", fi.Name, fi.Directory);
            }

            //改变目录。
            //在这种情况下,首先检查是否已经存在,如果不存在则创建它。    
            if (!System.IO.Directory.Exists(@"C:\Users\Public\TestFolder\"))
            {
                System.IO.Directory.CreateDirectory(@"C:\Users\Public\TestFolder\");
            }
            //将当期目录设定为指定目录
            System.IO.Directory.SetCurrentDirectory(@"C:\Users\Public\TestFolder\");
            currentDirName = System.IO.Directory.GetCurrentDirectory();
            Console.WriteLine(currentDirName);
            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
View Code

    DriveInfo只是获得驱动,GetDrives()只是获取所有逻辑驱动器的驱动器名称,和驱动相关的都可以用这个类来操作。

 Directory目录篇

   DirectoryInfo和Directory说白了就是读取指定目录下的文件目录,例如上面第一段代码中的递归方法,就是用来访问文件目录的。此类可以创建目录或删除目录,操作的只是目录而不是对象本身。目录通常由各种文件组成。

       常用的方法有:

  create()——创建目录。
  void Delete(string path,bool recursive)删除目录,recursive表示是否递归删除,如果recursive为false则只能删除空目录。
  bool Exists(string path) 判断目录是否存在
  string[] GetDirectories(string path)得到一个目录下的子目录
  string[] GetDirectories(string path,string searchPattern,SearchOption searchOption) 通匹配符查找子目录下的子目录,可以搜索到隐藏文件
  static string[] GetFiles(string path)得到一个父目录下的文件
  string[] GetFile(string path,string searchPattern,SearchOption searchOption) 通过匹配符查找目录下的文件
  DirectoryInfo GetParent(string path) 得到目录的父目录
  move()//移动、剪切。只能在同一个磁盘中。目录没有Cope方法。可以使用Move()方法实现重命名
  

  //可以指定一个目录当做对象来使用
  DireectoryInfo dirInfo=new DirectoryInfo(@"c:abc")
  //通过dirInfo对象,就可以获取c:\abc下的所有的文件与文件夹
  FileInfo[] finfos=dirInfo.GetFiles();
  DirectoryInfo[] dinfos=dirInfo.GetDirectories();

 实例代码:TreView上加载目录的代码,用的递归和过滤*.txt文件  

代码获取文件:
private void Load(object sender,EventArgs e)
{
//将Demo下的文件夹加载到TreeView上
//获取demo目录
DirectoryInfo demoDir = new DirectoryInfo("demo")'
//获取demo目录下的所有的直接子文件夹
DirectoryInfo[] dirInfos=demoDir.GetDirectories();
foreach(DirectoryInfo item in dirIndos)
{TreeNode tode=treeView1.Nodes.Add(item.Name);
LoadData(item.FullName,tnode);}}
//递归获取当前目录下的所有子目录和子文件
private void LoadDate(string path,TreeNode tnode)
{
//获取当前路径下的所有直接子文件
string[] files=Directory.GetFiles

(path,"*.txt",SearchOption.TopDirectoryOnly);
//把这些文件加到treeView上
foreach(string item in files)
{
TreeNode nodeFile=tnode.Nodes.Add(Path.GetFileName(item));
//当前文件的完整路径绑定到Tag中,Tag在每个控件中都有,没有意义就是为了存放//数据
nodeFile.Tag=item;
}
2.获取当前路径下的所有直接子文件夹
string[] dirs=Directory.GetDirectories(path);
foreach(string item in dirs)
{//Add方法的返回值就是,刚刚增加的这个节点
TreeNode node=tnode.Nodes.Add(Path.GetFileName(item));
LoadDate(item,node);
}}
View Code

  File文件篇

  文件编码:

  ASCLL英文码表,每个字符占1个字节。
  GB2312,兼容ASCLL。包含中文。每个英文占一个字节,中文占两个字节。
  GBK:简体中文,兼容gb2312,包含更多汉子。英文占1个字节,中文占两个。
  Big5繁体中文。
  Unicode:国际码表,中文英文都占两个字节。
  Utf-8:国际码表,英文占一个字节,中文占3个字节。  
  方法:
  void AppendAllText(string path,string contents),将文本contents附加到文件path中。
  bool Exists(string path)判断文件path是否存在。
  string[] ReadAllLine(string path)读取文本文件到字符串数组中。
  string ReadAlText(string path)读取文本文件到字符串中。
  void WriteAlText(string path,string contents)将文本contents保持到文件path中,会覆盖旧文件。
  WriteAllLine(string path,string[] contents),将字符串数组逐行保存到文件path中,会覆盖旧内容。
  File.Delete(@"c:\1.txt");//如果不存在也不报异常

  文件拷贝:

  拷贝文件的两种方式:

  将源文件全部读到内存中,在写到目标文件中;

  读取源文件的Nkb内存,写到目标文件中,在读取文件的Nkb内存,在写到目标文件中。(第二种方式就是一种流的的操作

  用File.ReadAllText、File.WriteAllText进行文件读写是一次性读写,如果文件非常大会占内存、慢。若需要读取一次处理一行
的机制,这就是流(Stream)。Stream会只读取要求的位置、长度的内容。Stream不会将所有内容一次性读取到内存中,还有一个指针
,指针指到哪里才能读、写到哪里。流有很多种,文件流是其中一种(还有二进制流、网络流等等)。常用FileStream类的new FileStream("c:/a.txt",filemode,fileaccess)后两个参数可选值。filestream可读可写。可以使用file.OpenReead、File.OpenWrite这两个简化调用方法。用byte[]是任何数据的最根本表示形式,任何数据最终都是二进制。filestream的Position属性为当前文件指针位置,没写一次就要移动一下Position以备下次写到后面的位置。Write用于向当前位置写入若干字节,Read用户读取若干字节。

  通过文件流读取磁盘上的文件:  

//1.创建文件流    FileMode可以选择打开文件的方式
FileStream fsRead=new FileStream("1.txt",FileMode.Open);
//2创建缓冲区
byte[] byts=new byte[fsRead.Length];
//开始读取   要byts读取到的数组中
int r=fsRead.Read(byts,0,btys.Length);
//关闭文件流,释放资源
fsRead.Colse();

//将byte文件转换为文字
string msg=System.Text.Encoding.UTF8.GetString(byts);
View Code

   通过文件流写入文件:  

//创建文件流
FileStream fs=new FileStream(@"c:\mywords.txt",FileMode.OpenOrCreate);
string msg="wenzi"//要写入的内容
//创建byte[]
byte[] byts= System.Text.Encoding.UTF8.GetBytes(msg);
//将byts中的内容写入文件中
fs.Write(byts,0,byts.Length);
//关闭文件流
fs.Flush();//清除下缓存,都写到硬盘中
fs.Close();
fs.Dispose();
View Code

  拷贝大文件代码 ,需要两个流:一个用来读,一个用来写。两个文件,两个文本。  

//创建一个读取文件的流
using(FileStream fsRead=new FileStream(source,FileMode.Open))
{
//创建一个写入文件的流
using(FileStream fsWrite=new FileStream(target,FileMode.Create))
{
//创建一个读取文件、写入文件的一个缓冲区
byte[] buffer=new byte[1024*1024 *10];//10M
//源文件的总字节数
long len=fsRead.Length;
//开始读取文件
while(true)
{
//返回值t表示本次实际读取到的字节数
int r=fsRead.Read(buffer,0,buffer.Length);
//将读取出来的buffer内容写入到fsWrite文件流中。
if(r<=0)//表示读取到了文件的末尾
{break;}
else{
//如果r>0,则表示本次读取到了内容
fsWrite.Write(buffer,0,r);
//已经拷贝了的文件长度
//long lenAlready=fsWrite.Length;
//double proc=(double)lenAlready/len;
double proc=(double)fsWrite.Position/len;  Position当前流的位置
Console.WriteLine("拷贝进度{0}",proc*100);
}}}}
View Code

  流里面有一个Position属性,用来确定读取到哪了:fsRead.Position。这个属性可以记得上次读取到哪了,再次读取时可以从上次读取的位置进行读取。

  用StreamWrite可以简化文本类型的Stream的处理。StreamReader是辅助Stream进行处理的,Steam读取的不能分辨出读取到哪,可能是半个字。StreamReader是读取文本一次只读取一行。StreamReader sr =new StreamReader("c:\writer.txt");
sr.EndOfStream //没有读取到文件流末尾,就继续读取
while(sr.ReadLine()!=null){
Console.WriteLine(sr.ReadLine());//判断处已经读取一次,所以会间接读取} 

  File类的方法:快速得到文件流
  FileStream fs=File.Open();    //返回FileStream
  FileStream fs=File.OpenRead();  //返回只读的FileStream
  FileStream fs=File.OpenWrite();    //返回只读的FileStream
  FileStream fs=new FileStream(参数); 

   获取当前文件的所在路径注意事项:如获取当前exe文件执行的路径。 两种方法: 

    Assembly.GetExecutingAssembly().Location;

  Application.StartupPath。
  注意:不要用Directory.GetCurrentDirectory();获取应用程序的当前工作目录,因为这个可能会变。如果设置OpenFileDialog对话框的路径的话,用Directory.GetCurrentDirectory()将得不到exe准确路径,这个需要特别注意下。

  //textBox2.Text=File.ReadAllText("1.txt",Encoding.Default);
  //获取当前的exe文件执行的路径
  string exePath=Assembly.GetExecutingAssembly().Location.ToString();
  string txtPath =Path.Combine(Path.GetDirectoryName(exePath),"1.txt");
  textBox2.Text=File.ReadAllText(txtPath,Encoding.Default);

Path类部分

  对包含文件或目录路径信息的 String 实例执行操作。 这些操作是以跨平台的方式执行的。
  Path类中方法,基本都是对字符串的操作,与实际文件都没关系。在Path类中修改文件的路径是没有作用的。Path类,目录和文件操作的命名空间System.IO;

  常用的Path方法:
  Path.GetTempPath();   //获取当前用户的临时目录
  Path.GetTempFileName();//获取一个随机额文件名,并在临时目录下创建这个文件
  Path.GetRandomFileName();//获取一个随机的文件名(也可以用作文件名。
  string ChangeExtension(string path,string axtension)    //修改文件的后缀,"修改"支持字符串层面的,没有真的给文件改名
  string Combine(String path1,stirng path2) //将两个路径合并成一个路径 ,比用+好,可以方便解决不加斜线的问题,自动处理路径分隔符的问题,如:string s=Path Combine(@"c:temp","a.jpg")


  string GetDirectoryName(string Path){}得到文件的路径名,Path.GetDirectoryName(@"c:temo\a.jpg")
  string GetExtension(string path) 得到文件的扩展名
  string GetFileName(string path) 得到文件路径的文件名部分
  string GetFileNameWithoutExtension(string path)得到去除扩展名的文件名
  string GetFullPath(string path)得到文件的全路径,可以根据相对路径获得绝对路径

  Path.Combine(path1,path2)如果指定的路径之一是零长度字符串,则该方法返回其他路径。如果path2包含绝对路径,则该方法返回path2.
如果path1不是以分隔符结束,并且不是c:或d:等则在串联钱path1增加\分隔符。
  分隔符:(与操作平台有关)
  Path.DirectorySeparatorChar→\
  Path.PathSeparator→;
  Path.VolumeSeparatorChar→:
  Path.GetFileName()
  获取文件名当目录为c:\windows\test时,可获取最后一个目录名,但当目录路径为c:\windows\test\时,不可以。
流部分

 stream(所有流的父类,是一个抽象类)

 可以查看此处:http://www.cnblogs.com/Johnny_Z/archive/2011/09/12/2174148.html,不想再整了。没必要整写重复的东西。

序列化部分

  参考:http://msdn.microsoft.com/zh-cn/library/7ay27kt9(v=vs.90).aspx  

  序列化是将对象状态转换为可保持或传输的形式的过程。序列化的补集是反序列化,后者将流转换为对象。这两个过程一起保证数据易于存储和传输。

  .NET Framework 提供了两个序列化技术:

 

  • 二进制序列化保持类型保真,这对于多次调用应用程序时保持对象状态非常有用。例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和网络等。远程处理使用序列化,“按值”在计算机或应用程序域之间传递对象。

  • XML 序列化只序列化公共属性和字段,并且不保持类型保真。当您希望提供或使用数据而不限制使用该数据的应用程序时,这一点非常有用。由于 XML 是开放式的标准,因此它对于通过 Web 共享数据来说是一个理想选择。SOAP 同样是开放式的标准,这使它也成为一个理想选择。

  如何序列化:http://msdn.microsoft.com/zh-cn/library/szzyf24s(v=vs.90).aspx

      如何反序列化:http://msdn.microsoft.com/zh-cn/library/fa420a9y(v=vs.90).aspx

  小总:哪些类可被序列化?
  1.必须本身是可序列化的(Person类型标记为可序列化)
  2.类中所有的字段属性的类型也必须标记为可序列化的
  3.当前类型的所有父类也必须标记为可序列化的。

  例如:
    [Serializable]
    public calss Person
    {//2.建议,在使用序列化的时候尽量不要使用自动化属性,因为自动属性
    ,每次编译的时候自动生成的字段名可能不一样,所以在反序列化的时候可能会造成问题
    Public string Name//自动属性
    {get;set;}}
  反序列化
    BinaryFormatter bf=new BinaryFormatter();
    using(FileStream fs=new FileStream("person.bin",FileMode.Open))
    {2.执行反序列化
    Person per=(Person)bf.Deserialize(fs);}
  在执行反序列化的时候,由于person.bin中存储的是原来Person类型序列化后的结果,所以要对person.bin反序列化时,需要person类所在的程序集。
  问题:为什么反序列化的时候需要原来定义Person类的那个程序集?
  反序列化的时候,要返回一个对象,这个对象必须根据原来Person所在的程序集才能创建,也就是说person.bin中所包含的仅仅是一些数据,根据这些数据是无法在内存中创建对象的。只能是根据原来Person类型的程序集来创建对象,然后Person.bin中的数据——对应进行赋值。


  对象序列化,只是对对象的一些状态信息进行序列化(比如:字段)。
  对于方法之类的根本不能进行序列化,也就是说person.bin中只包含字段名和字段值,根本没有 方法信息。但是反序列化的时候返回的是一个对象,那么只根据这些字段信息是无法创建对象的,所以这个时候需要先根据原来的程序集,获取Person类型相信n对象,然后再把反序列化得到的字段信息赋值给这个Person对象。

  把对象当做字节流。
  对象序列化是将对象(比如Person对象)转换为二进制数据(字节流),反序列化是将二进制数据还原为对象。对象是稍纵即逝的,不仅程序重启、操作系统重启会造成对象的消失,就是对出函数范围等都可能会造成对象的消失,序列化/反序列化就是为了保持对象的持久化。就像用dv录像(序列化)和用播放器播放(反序列化)一样。
  对象序列化,只能针对对象的字段进行序列化。
  BinaryFormatter类有两个方法;
  void Seriallize(Stream stream,object graph)对象graph序列化到stream中。
  object Deseriallize(Stream stream)将对象从stream中反序列化,返回值为反序列化得到的对象。
  不是所有对象都能序列化,只有可序列化的对象才能序列化,在类声明上添加[Serializable],对象的属性、字段的类型也必须可序列化。

  关于二进制序列化需要注意的事项:
  1.要序列化的类型必须标记为:【Serializable】
  2.该类型的父类也必须标记为:【Serializable】
  3.该类型中的所有成员的类型也必须标记为:【Serializable】
  4.序列化只会对类中的字段序列化。(只能序列化一些状态信息)
  5.不建议使用自动属性。(每次生成的字段都可能不一样,影响反序列化)
  为什么要序列化?
  将一个复杂的对象转换流,方便我们的存储于信息交换。

  序列化的应用:

    • 序列化的应用:Asp.Net、ViewState、WCF、.Net Remoting、Asp.Net Cache、集群等。
    • 将对象持久化存储、磁盘、数据库
    • 将对象复制到剪贴板
    • 将对象通过网络传输
    • 将对象备份(深拷贝)

  对象的序列化和反序列化:
  1.创建一个二进制序列化器:
  BinaryFormatter bf=...;
  2.创建一个文件流
  3.bf.Serialize(stream,对象);

     更多参考:http://msdn.microsoft.com/zh-cn/library/7ay27kt9(v=vs.90).aspx

 

posted @ 2014-05-21 17:21  anlien  阅读(436)  评论(0编辑  收藏  举报