zoukankan      html  css  js  c++  java
  • C# 篇基础知识6——文件和流

    计算机以文件的形式把数据存储在磁盘、光盘等存储设备上。文件的管理和操作是操作系统的一个重要组成部分,.NET 框架提供了一组功能强大的类,可以方便地对文件进行操作和管理。

    1.文件操作相关的类

    用于文件操作的类位于System.IO 命名空间中,用这些类可以方便地对文件进行创建、读写、复制、删除、移动、打开等操作。

    2.File类和FileInfo类

    命名空间 System.IO 中的File 类用于对文件进行创建、打开、复制、移动、删除、重命名等典型操作,并能获取或设置文件的属性信息。File 类中所有的方法都是静态的,使用起来非常简单,File 类的部分方法如下图所示:

    File类的方法使用起来非常方便:

    using System;

    using System.IO;

    static void Main(string[] args)

    {  string path = @"D: est.txt";

    if(File.Exists(path)) //如果文件已经存在,则读取文件内容

    {  //读取文件

    string contents = File.ReadAllText(path);

    Console.WriteLine("读取文件: " + contents);  }

    else //如果文件不存在,则创建文件并写入内容

    {  string contents = "无可奈何花落去, 似曾相识燕归来, 小园香径独徘徊。";

    File.WriteAllText(path, contents); //写入文件

    Console.WriteLine("文件已写入。" );  }

    }

    注意 WriteAllText()、WriteAllLines()和WriteAllBytes()方法都会覆盖以前的文件,使用时要特别小心。要想在文件尾部追加新文本,请使用AppendAllText()方法。

    FileInfo 类和File 类相似,可以创建、复制、移动、删除文件,可以获取或设置文件的属性,只是少了读写文件的功能。FileInfo 类的成员方法都是非静态的,使用方法前需要先创建一个FileInfo 类的对象,然后通过FileInfo 对象调用方法。当频繁操作某文件时,使用FileInfo 类的效率会更高。

    (1)关于文件的异常

    操作文件时经常会发生异常,比如指定文件不存在,路径无效,没有权限等。关于文件的异常的方法如图示,因此关于文件的操作一般要放在 try-catch 结构中,以处理可能发生的异常情况。

    try

    {  string path = @"D: est.txt";

    //如果文件已经存在,则读取文件内容

    if(File.Exists(path))

    {  //读取文件

    string contents = File.ReadAllText(path);

    Console.WriteLine("读取文件: " + contents);

    }

    else //如果文件不存在,则创建文件并写入内容

    {

    string contents = "无可奈何花落去, 似曾相识燕归来, 小园香径独徘徊。";

    //写入文件

    File.WriteAllText(path, contents);

    Console.WriteLine("文件已写入。" );

    }

    }

    catch (Exception e)

    {  //异常处理

    Console.WriteLine(e.Message);

    }

    3.Directory类和DirectoryInfo类

    System.IO 命名空间中的Directory 类用于执行对目录(文件夹)的操作,比如创建、移动、删除、重命名等,也可通过它获取或设置目录的属性。Directory 类的部分方法如图所示,所有 Directory 类中的方法都是静态的,使用起来非常方便。

    using System;

    using System.IO;

    static void Main(string[] args)

    {  try

    {  string path = @"D:Program FilesWindows Media Player";

    if (Directory.Exists(path)

    {  //获取子目录

    string[] dirs = Directory.GetDirectories(path);

    Console.WriteLine("子目录:");

    foreach (string dir in dirs) {

    Console.WriteLine(dir); }

    //获取文件

    string[] files = Directory.GetFiles(path);

    Console.WriteLine("文件:");

    foreach (string file in files) {Console.WriteLine(file);}

    }

    else{Console.WriteLine("目录不存在");}

    }

    catch (Exception e)

    {//异常处理Console.WriteLine(e.Message);}

    }

    操作结果所图所示,DirectoryInfo 类和Directory 类功能相似,区别是DirectoryInfo 类的成员方法都是非静态的,使用方法前需要先创建一个DirectoryInfo 类的对象。当频繁操作某目录时,使用DirectoryInfo 类的效率会更高。

    4.path类

    通过 System.IO 命名空间中的Path 类,我们可以方便的处理路径。Path 类的部分字段和方法如图所示。Path 类中的很多字段是和操作系统关联的,在不同的操作系统中可能有不同的结果。比如DirectorySeparatorChar,在Windows 和Macintosh 操作系统中的值是“”,在Unix 操作系统中为“/”;VolumeSeparatorChar 在Windows 和Macintosh 操作系统中为“:”,在Unix操作系统中为“/”;PathSeparator 在 Windows 操作系统中默认值是“;”,在 Unix 操作系统上为“:”。

    using System.IO;

    //输出分隔符

    Console.WriteLine("DirectorySeparatorChar" + Path.DirectorySeparatorChar);

    Console.WriteLine(" PathSeparator" + Path.PathSeparator);

    Console.WriteLine(" VolumeSeparatorChar" + Path.VolumeSeparatorChar);

    //输出路径信息

    string path = @"D:Program FilesWindows Media Playerwmplayer.exe";

    Console.WriteLine(" GetFileName" + Path.GetFileName(path));

    Console.WriteLine(" GetFileNameWithoutExt" + Path.GetFileNameWithoutExtension(path));

    Console.WriteLine(" GetExtension" + Path.GetExtension(path));

    Console.WriteLine(" GetDirectoryName" + Path.GetDirectoryName(path));

    结果如图所示:

    (1)Environment类

    通过 System 命名空间中的Environment 类,可以方便地获取与系统相关的信息。Environment 类的部分属性和方法如图所示。

    一个例子:

    //显示系统信息

    Console.WriteLine(" 处理器数量: " + Environment.ProcessorCount);

    Console.WriteLine(" 操作系统版本: " + Environment.OSVersion);

    Console.WriteLine("公共语言运行库版本: " + Environment.Version);

    Console.WriteLine(" 系统目录: " + Environment.SystemDirectory);

    Console.WriteLine(" 用户域名: " + Environment.UserDomainName);

    Console.WriteLine(" 用户名: " + Environment.UserName);

    Console.WriteLine(" 机器名: " + Environment.MachineName);

    通过 Environment 类的GetFolderPath()方法可以获取系统特殊文件夹的路径,这些特殊文件夹由SpecialFolder 枚举列出。SpecialFolder 类的部分成员如图所示。

    例子:

    string d = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

    string p = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);

    string m = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);

    string f = Environment.GetFolderPath(Environment.SpecialFolder.Favorites);

    //输出特殊目录路径

    Console.WriteLine("我的文档:" + d);

    Console.WriteLine("我的图片:" + p);

    Console.WriteLine("我的音乐:" + m);

    Console.WriteLine("我的收藏:" + f);

    5.基于流的文件操作

    数据以文件的形式存储在硬盘、光盘等存储介质上,读写数据的过程可以看作数据像水一样流入或流出存储介质,所以.NET 设计了一种叫做流(Stream)的类来读写文件。实际上一旦我们打开一个文件,它就和一个流关联起来。文件是数据源,流是传输数据的工具,通过这种专门的工具来传输数据,就可以使传输工具和数据源分离开来,从而更容易切换数据源,更容易实现不同环境下代码的重用。数据源除了文件以外,还可以是内存中数据、网络上的数据甚至是代码中的一个变量等等。与流相关的类都定义在 System.IO 命名空间中,它们大多数继承于抽象类Stream。如图展示了部分与流相关的类。

    FileStream 类用于字节数据输入和输出,TextReader 和TextWriter 用于Unicode 字符的输入和输出,BinaryReader 和BinaryWriter 用于二进制数据的输入和输出。

    (1)FileStream类

    FileStream 是专门进行文件操作的流,使用它可对文件进行读取、写入、打开和关闭等操作,既支持同步读写操作,也支持异步(缓冲)读写操作。FileStream 类的部分属性和方法如图所示:

    要使用流,必须先创建一个流的对象。请创建一个名为“StreamTest”的控制台项目,并添加如下代码。

    string fileName = @"D:filestream_test.data";

    //若文件不存在,则创建文件,写入数据

    if(!File.Exists(fileName))

    {  FileInfo myFile = new FileInfo(fileName); //创建文件

    FileStream fs = myFile.OpenWrite(); //获取与文件对应的流

    byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };

    fs.Write(datas, 0, datas.Length); //用流写入数据

    Console.WriteLine("数据已写入。");

    fs.Close(); //关闭流

    }

    else //若文件存在,则读取数据

    {   //建立与文件关联的流

    FileStream fs = new FileStream(fileName, FileMode.Open, File Access.Read);

    byte[] datas = new byte[fs.Length];

    fs.Read(datas, 0, datas.Length); //用流读取数据

    Console.WriteLine(“读取数据:”);

    foreach(byte data in datas)

    {  Console.Write(data);}

    fs.Close();  //关闭流

    }

    这里我们用两种方法获取与文件对应的流。第一种办法是通过FileInfo类的OpenWrite()方法获取,OpenWrite()方法打开相应的文件,返回一个与该文件相关的FileStream对象。

    当使用FileStream类的构造函数时,需要提供打开方式(FileMode)、访问模式(FileAccess)、共享模式(FileShare)等信息,它们分别用FileMode枚举、FileAccess枚举和FileShare枚举表示,这些枚举的值很容易理解,更详细的信息请参看帮助文档。与FileStream类相关的枚举如图所示:

    FileStream类的Write()方法和Read()方法。Write()方法的原型如下,参数中字节数组array 是数据源,offset 和count 表示把数据源中的从位置offset 开始的count 个字节写入文件。

    Read()方法的原型如下:参数中字节数组array 是目标数组,用于存储读取到的数据,存储的位置是从位置offset开始的count 个字节。

    返回值为实际读取到的字节数。如果开始读取时当前位置已在流的末尾,则返回值为0。如果流中当前可用的字节数没有请求的字节数那么多,则实际读取到的字节数会小于请求的字节数。还有一个需要注意的情况是,在处理网络流(如FTP)、设备流(如串口输入)等流时,即使尚未到达流的末尾,实际读取到的字节数仍有可能少于请求的字节数。如果是从控制台读取或者从本地文件读取,很少遇到这种情况。

    综上可以看出,FileStream 类主要用于向文件中写入或读取以字节为单位的数据。

    2)关于流的异常

    try-catch-fianlly结构,用流来操作文件也可能会出现异常,所以关于流的操作一般要放在try-catch-fianlly 结构中,以增强程序的健壮性。

    string fileName = @"D:filestream_test.data";

    FileStream fs = null;

    try

    {…}

    catch(Exception e)

    { Console.WriteLine(e.Message);}

    finally

    { if (fs!=null)  fs.Close(); }

    注意,fs对象的声明代码要放在try语句之前,不然catch块和fianly块中无法识别。另外关闭流的操作应放在fianlly块中,以确保无论操作成功还是操作失败,流都被及时释放。

    读取一个文件的内容时,真正有用的代码往往只有几行,然而为了健壮性,我们却要写上10多行的代码来处理异常和关闭文件。如何才能减少这种麻烦呢?一种办法是自己编写一个方法,把异常处理和关闭文件的代码封装进去。下面编写了一个处理文件的通用方法UniversalProcess(),只要告诉它文件路径和处理文件的具体代码,它就会自动完成异常处理和关闭文件的操作。

    public delegate void MyFileProcessCode(FileStream file);

    //封装文件处理

    public static void UniversalProcess(string path,MyFileProcessCode doSomething)

    { FileStream fs=new FileStream(path,FileMode.Open,FileAccess.ReadWrite);

         try {  doSomething(fs);}

         catch(Exception e)  {Console.WriteLine(e.Message);}

         finally{if(fs!=null) fs.Close();}

    }

    可以直接通过匿名函数和一个文件地址进行访问,例如:

    UniversalProcess(@"D:a.text", delegate(FileStream fs)

    {

    byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };

    fs.Write(datas, 0, datas.Length);

    });

    6.using语句

    为了防止出现同步问题,当一个程序读写文件时,操作系统通常都会阻止其他程序读写该文件,因此使用完毕后要及时关闭,否则就会导致其他程序不能使用该文件。除了可以在fianlly块中关闭文件(流)外,我们还可以用C#提供的using语句进行文件操作。

    using(FileStream fs = File.OpenWrite(path))

     {

    byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };

    fs.Write(datas, 0, datas.Length);

    }

    在上面的代码在using后的括号里创建了一个流对象fs,然后在using后的语句块中使用该流对象。当退出using语句时,系统会自动关闭流对象fs,使用起来非常方便。using语句适用于那些需要及时释放资源的代码,其一般形式为:

    using(type obj = initialization)

    { //具体处理代码}

    在using后的括号中创建一个对象obj,然后在后面的大括号中使用它。当退出using语句时,对象obj会被及时销毁。using语句实际上是对try语句的封装,等价于:

    { type obj=initialization;

     try {//具体处理代码}

     finally{ if (obj!=null) { ((IDisposable)obj).Dispose();}}

    }

    程序先执行try块中的代码,不管是否发生异常,系统都会自动执行finally块中的代码,调用obj对象的Dispose()方法,销毁对象,释放资源。Dispose()方法和Close()方法的功能基本上是一样的,都是用来销毁对象。实际上,Close()方法就是通过调用Dispose(),方法实现的。

    由此看出 using 语句不光适用于处理关于流的操作,它也适用于任何包含Dispose()方法且需要及时销毁的的对象。using 语句使用起来非常方便,如果你查看帮助文档,就会发现涉及流的例子都是用using 语句编写的。

    7.用流读写文本文件

    文本文件主要通过 StreamReader 和StreamWriter 来实现读写,它们分别继承于抽象类TextReader 和TextWriter。StreamReader 类的部分属性和方法,StreamWriter 类的部分属性和方法,分别所图示。

    要使用 StreamReader 和StreamWriter,也必须先创建它们的对象。

    StreamWriter streamWriter = null;

    StreamReader streamReader = null;

    try

    {

    //若文件不存在,则创建文件,写入数据

    if(!File.Exists(@"D: ext_test.txt"))

    {

    FileInfo myFile = new FileInfo(@"D: ext_test.txt"); //创建文件

    streamWriter = myFile.CreateText(); //获取文件对应的流

    string text = @"何处望神州,满眼风光北固楼。千古兴亡多少事,悠悠。不尽长江滚滚流。年少万兜鍪,坐断东南战未休。天下英雄谁敌手?曹刘。生子当如孙仲谋。";

    streamWriter.Write(text); //用流写入数据

    Console.WriteLine("数据已写入.");

    }

    else//若文件存在,则读取数据

    {  //建立与文件关联的流

    streamReader = new StreamReader(@"D: ext_test.txt");

    string text = streamReader.ReadToEnd(); //用流读取数据

    Console.WriteLine("读取数据: " + text);

    }

    }

    catch (Exception e)

    {  //异常处理

    Console.WriteLine(e.Message);

    }

    finally

    {  //关闭流

    if(streamWriter!=null)streamWriter.Close();

    if(streamReader!=null)streamReader.Close();

    }

    这里我们用两种方法获取与文件对应的流。第一种办法是通过FileInfo 类(或File 类)的方法获取,比如FileInfo 对象myFile的CreateText()方法返回一个StreamWriter 流:StreamWriter streamWriter =myFile.CreateText(); OpenText ()方法返回StreamReader 流:StreamReader streamReader=myFile.ReadText(); 。File 类的部分与流相关的方法如图所示。

    第二种办法是用 StreamReader 类或StreamWriter 类的构造函数创建流。

    StreamReader streamReader = new StreamReader(fileName);

    8.用流读写二进制文件

    通过 BinryReader 和BinaryWriter 类实现二进制读写。BinryReader 类的部分属性和方法,BinaryWriter 类的部分属性和方法,分别如图所示:

    要使用 BinryReader 和BinaryWriter 类,也必须先创建它们的对象,但它们的对象总是通过FileStream 对象来创建的。

    string path = @"D:inary_test.txt";

    FileStream fileStream = null;

    try

    {  //创建文件

    fileStream = File.Create(path);

    //写文件

    BinaryWriter bw = new BinaryWriter(fileStream);

    DateTime time = DateTime.Now;

    bw.Write(time.Year);

    bw.Write(time.Month);

    bw.Write(time.Day);

    bw.Write(time.Hour);

    bw.Write(time.Minute);

    bw.Write(time.Second);

    //当前位置移动到开头

    fileStream.Seek(0, SeekOrigin.Begin);

    //读文件

    BinaryReader br = new BinaryReader(fileStream);

    int year = br.ReadInt32();

    int month = br.ReadInt32();

    int day = br.ReadInt32();

    int hour = br.ReadInt32();

    int minute = br.ReadInt32();

    int second = br.ReadInt32();

    Console.WriteLine("{0}-{1}-{2} {3}:{4}:{5}",

    year, month, day, hour, minute, second);

    }

    catch (Exception e)

    {

    //异常处理

    Console.WriteLine(e.Message);

    }

    finally

    {

    //释放资源

    if(fileStream!=null)

    fileStream.Close();

    }

    实际上,BinryReader 和BinaryWriter 本身并不是独立的流,它们是对其他流的包装,所以创建BinryReader 和BinaryWriter 对象时,需要传给它们一个FileStream 对象,对BinryReader 和BinaryWriter 的操作,就是对FileStream 的操作。

     

    见贤思齐,见不贤而自省
  • 相关阅读:
    Leetcode 811. Subdomain Visit Count
    Leetcode 70. Climbing Stairs
    Leetcode 509. Fibonacci Number
    Leetcode 771. Jewels and Stones
    Leetcode 217. Contains Duplicate
    MYSQL安装第三步报错
    .net 开发WEB程序
    JDK版本问题
    打开ECLIPSE 报failed to load the jni shared library
    ANSI_NULLS SQL语句
  • 原文地址:https://www.cnblogs.com/Sweepingmonk/p/10867731.html
Copyright © 2011-2022 走看看