zoukankan      html  css  js  c++  java
  • 文件夹和文件、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:UsersPublicTestFolder"))
                {
                    System.IO.Directory.CreateDirectory(@"C:UsersPublicTestFolder");
                }
                //将当期目录设定为指定目录
                System.IO.Directory.SetCurrentDirectory(@"C:UsersPublicTestFolder");
                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:temoa.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 est时,可获取最后一个目录名,但当目录路径为c:windows est时,不可以。
    流部分

     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

  • 相关阅读:
    字典序(摘)
    N
    Y
    B
    购物网站和秒杀系统实现技术
    行测技巧:十字交叉法解决比值混合问题
    数学符号读法大全
    搜狐畅游编程题
    sql删除重复记录
    'for' loop initial declarations are only allo
  • 原文地址:https://www.cnblogs.com/anlen/p/3741720.html
Copyright © 2011-2022 走看看