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 的操作。

     

    见贤思齐,见不贤而自省
  • 相关阅读:
    常用集体名词的用法
    囊中羞涩的表达
    《当幸福来敲门》观后感
    <肖申克的救赎>观后感
    心语4
    补充:回答网友的问题,如何不用路径,而直接将CImage画到DC中,之后DC一起显示.
    线程中对变量的用法
    添加按键变量数组,就是很多同种类型按键关联变量,这些变量是一个数组;
    不容按钮、下拉框 执行同一个函数或者同一种函数的用法
    CImage显示位图与CDC双缓冲冲突,使用路径层解决.
  • 原文地址:https://www.cnblogs.com/Sweepingmonk/p/10867731.html
Copyright © 2011-2022 走看看