• C#(99):文件读写(三)利用FileStream类操作字节数组byte[]、BinaryFormatter、内存流MemoryStream


    一、Stream类概述

          在.NET Framework中,文件和流是有区别的。文件是存储在磁盘上的数据集,它具有名称和相应的路径。当打开一个文件并对其进行读/写时,该文件就称为流(stream)。但是,流不仅仅是指打开的磁盘文件,还可以是网络数据。.Net Framework允许在内存中创建流。此外,在控制台应用程序中,键盘输入和文本显示都是流。

    属性

    • Length    当在派生类中重写时,获取流长度(以字节为单位)。
    • Position    当在派生类中重写时,获取或设置当前流中的位置。
    • ReadTimeout    获取或设置一个值(以毫秒为单位),该值确定流在超时前尝试读取多长时间。
    • WriteTimeout    获取或设置一个值(以毫秒为单位),该值确定流在超时前尝试写入多长时间。

    方法

    • Seek(Int64, SeekOrigin)    当在派生类中重写时,设置当前流中的位置。
    • Read(Byte[], Int32, Int32)    当在派生类中重写时,从当前流读取字节序列,并将此流中的位置提升读取的字节数。
    • ReadByte()    从流中读取一个字节,并将流内的位置向前提升一个字节,或者如果已到达流结尾,则返回 -1。
    • Write(Byte[], Int32, Int32)    当在派生类中重写时,向当前流中写入字节序列,并将此流中的当前位置提升写入的字节数。
    • WriteByte(Byte)    将一个字节写入流内的当前位置,并将流内的位置向前提升一个字节。
    • CopyTo(Stream, Int32)    使用指定的缓冲区大小,从当前流中读取字节并将其写入到另一流中。
    • Close()    关闭当前流并释放与之关联的所有资源(如套接字和文件句柄)。 不直接调用此方法,而应确保流得以正确释放。
    • Flush()    当在派生类中重写时,将清除该流的所有缓冲区,并使得所有缓冲数据被写入到基础设备。

    二、 操作流的类

      1. Stream类

          Stream类是所有流的抽象基类。

          Stream类的主要属性有CanRead(是否支持读取)、CanSeek(是否支持查找)、CanTimeout(是否可以超时)、CanWrite(是否支持写入)、Length(流的长度)、Position(获取或设置当前流中的位置)、ReadTimeout(获取或设置读取操作的超时时间)、WriteTimeout(获取或设置写操作的超时时间),

          Stream类的主要方法有BeginRead(开始异步读操作),BeginWrite(开始异步写操作)、Close(关闭当前流)、EndRead(结束异步读操作)、EndWrite(结束异步写操作)、Flush(清除流的所有缓冲区并把缓冲数据写入基础设备)、Read(读取字节序列)、ReadByte(读取一个字节)、Seek(设置查找位置)、Write(写入字节序列)、WriteByte(写入一个字节)。

      2. FileStream、MemoryStream和BufferedStream,NetworkStream

    •   文件流类FileStream:以流的形式读、写、打开、关闭文件。另外,它还可以用来操作诸如:管道、标准输入/输出等其他与文件相关的操作系统句柄。
    •   内存流MemoryStream类:用来在内存中创建流,以暂时保持数据,因此有了它就无须在硬盘上创建临时文件。它将数据封装为无符号的字节序列,可以直接进行读、写、查找操作。
    •   缓冲流BufferedStream类:表示把流先添加到缓冲区,再进行数据的读/写操作。缓冲区是存储区中用来缓存数据的字节块。使用缓冲区可以减少访问数据时对操作系统的调用次数,增强系统的读/写功能。
    •   网络流NetworkStream类:为网络访问提供数据的基础流。

      注意,FileStream类也有缓冲功能,在创建FileStream类的实例时,只需要指定缓冲区的大小即可。

    三、文件流类 FileStream

          文件流类FileStream公开了以文件为主的Stream,既支持同步读/写操作,也支持异步读/写操作。

          FileStream类的特点是操作字节和字节数组。这种方式不适合操作用字符数据构成的文本文件,适合处理非文本文件。FileStream类提供了对文件的低级而复杂的操作,因此能够实现更多高级的功能。

    1、读文件

    Read,ReadByte()

    //创建d:file.txt的FileStream对象
    FileStream fstream = new FileStream(@"d:file.txt", FileMode.OpenOrCreate);
    byte[] bData = new byte[fstream.Length];
    //设置流当前位置为文件开始位置
    fstream.Seek(0, SeekOrigin.Begin);
    
    //设置流当前位置为文件开始位置
    fstream.Seek(0, SeekOrigin.Begin);
    //将文件的内容存到字节数组中(缓存)
    fstream.Read(bData, 0, bData.Length);
    string result = Encoding.UTF8.GetString(bData);
    Console.WriteLine(result);
    if (fstream != null)
    {
        //清除此流的缓冲区,使得所有缓冲的数据都写入到文件中
        fstream.Flush();
        fstream.Close();
    }

    将字节数组转成字符串显示以外,字节数组还可以

    //1、显示成图片
    MemoryStream mstream = new MemoryStream(bData);
    Image img = Image.FromStream(mstream);
     //2、作为值存储到数据库
    SqlCommand comm = new SqlCommand();
     comm.Parameters.Add("images", SqlDbType.Image).Value = bData;
     //3、写入文件
    File.WriteAllBytes("c:aa.txt", bData);
     FileStream fstream = new FileStream("c:aa.txt");
     fstream.Write(bData, 0, bData.Length);

    2、分块读文件

    int bufferSize = 5;
    //创建d:file.txt的FileStream对象
    FileStream fstream = new FileStream(@"d:file.txt", FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read, bufferSize, false);//false表示同步读
    byte[] bData = new byte[bufferSize];
    //设置流当前位置为文件开始位置
    fstream.Seek(0, SeekOrigin.Begin);
    int bytesRead;
    do
    {
        //将文件的内容存到字节数组中(缓存)
        bytesRead = fstream.Read(bData, 0, bData.Length);
        string result = Encoding.UTF8.GetString(bData, 0, bytesRead);
        Console.WriteLine(result);
    } while (bytesRead > 0);
    
    if (fstream != null)
    {
        //清除此流的缓冲区,使得所有缓冲的数据都写入到文件中
        fstream.Flush();
        fstream.Close();
    }

    3、异步读文件

    ManualResetEvent mEvent = new ManualResetEvent(false);
    int bufferSize = 5;
    byte[] bData = new byte[bufferSize];
    FileStream fstream = new FileStream(@"d:file.txt", FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read, bufferSize, true);//false表示异步读
    AsyncCallback callback = null;
    callback = (IAsyncResult ar) =>
      {
          int bytesRead = fstream.EndRead(ar);
          Console.WriteLine(Encoding.UTF8.GetString(bData, 0, bytesRead));
          if (bytesRead > 0)
          {
              fstream.BeginRead(bData, 0, bufferSize, callback, null);//继续读
          }
          else
          {
              fstream.Close();
              mEvent.Set();//读取完毕,发送信号
          }
      };
    
    IAsyncResult async = fstream.BeginRead(bData, 0, bufferSize, callback, null);
    mEvent.WaitOne(5000, false);
    Console.WriteLine("读取完成");

    4、IAsyncResult 接口

    • AsyncState    获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。
    • AsyncWaitHandle    获取用于等待异步操作完成的 WaitHandle。
    • CompletedSynchronously    获取一个值,该值指示异步操作是否同步完成。
    • IsCompleted    获取一个值,该值指示异步操作是否已完成。

    4、写文件

    Write,WriteByte(Byte)
    //创建d:file.txt的FileStream对象
    FileStream fstream = new FileStream(@"d:file.txt", FileMode.OpenOrCreate);
    byte[] bData = Encoding.UTF8.GetBytes("test filestream");
    //设置流当前位置为文件开始位置
    fstream.Seek(0, SeekOrigin.Begin);
    //将字节数组中的内容写入文件
    fstream.Write(bData, 0, bData.Length);
    if (fstream != null)
    {
        //清除此流的缓冲区,使得所有缓冲的数据都写入到文件中
        fstream.Flush();
        fstream.Close();
    }

    四、以二进制格式序列化对象BinaryFormatter

    1、SoapFormatter(用于HTTP中)和BinaryFormatter(用于TCP中)类实现了IFormatter接口 (由继承IRemotingFormatter,支持远程过程调用 (Rpc))

    • Deserialize(Stream)    反序列化所提供流中的数据并重新组成对象图形。
    • Serialize(Stream, Object)    将对象或具有给定根的对象图形序列化为所提供的流。

    XML序列化见:https://www.cnblogs.com/springsnow/p/9469399.html

    2、举例:

    [Serializable]
    public class Product //实体类
    {
        public long Id;
        [NonSerialized]//标识不序列化此成员Name
        public string Name;
        public Product(long Id, string Name)
        {
            this.Id = Id;
            this.Name = Name;
        }
    }
    
    static void Main()
    {
        //序列化(对象保存到文件)
        List<Product> Products = new List<Product> {
            new Product(1,"a"),new Product(2,"b")
        };
    
        FileStream fs = new FileStream("DataFile.dat", FileMode.Create);
        IFormatter formatter = new BinaryFormatter();
        formatter.Serialize(fs, Products);
        fs.Close();
    
        //反序列化(文件内容转成对象)
        FileStream fs1 = new FileStream("DataFile.dat", FileMode.Open);
        BinaryFormatter formatter1 = new BinaryFormatter();
        List<Product> addresses = (List<Product>)formatter1.Deserialize(fs1);
        fs1.Close();
        foreach (Product de in addresses)
        {
            Console.WriteLine("{0} lives at {1}.", de.Id, de.Name);
        }
    }

    五、内存流MemoryStream

    内存流相对字节数组而言,具有流特有的特性,并且容量可自动增长。

    在数据加密以及对长度不定的数据进行缓存等场合,使用内存流比较方便。下面的代码示例演示如何读取和写入将内存用作后备存储的数据。

    int count;
    UnicodeEncoding uniEncoding = new UnicodeEncoding();
    
    // Create the data to write to the stream.
    byte[] firstString = uniEncoding.GetBytes("Invalid file path characters are: ");
    byte[] secondString = uniEncoding.GetBytes(Path.GetInvalidPathChars());
    
    using (MemoryStream memStream = new MemoryStream(100))
    {
        // Write the first string to the stream.
        memStream.Write(firstString, 0, firstString.Length);
    
        // Write the second string to the stream, byte by byte.
        count = 0;
        while (count < secondString.Length)
        {
            memStream.WriteByte(secondString[count++]);
        }
    
        // Write the stream properties to the console.
        Console.WriteLine("Capacity = {0}, Length = {1}, Position = {2}
    ", memStream.Capacity.ToString(), memStream.Length.ToString(), memStream.Position.ToString());
    
        // Set the position to the beginning of the stream.
        memStream.Seek(0, SeekOrigin.Begin);
    
        // Read the first 20 bytes from the stream.
        byte[] byteArray = new byte[memStream.Length];
        count = memStream.Read(byteArray, 0, 20);
    
        // Read the remaining bytes, byte by byte.
        while (count < memStream.Length)
        {
            byteArray[count++] = Convert.ToByte(memStream.ReadByte());
        }
    
        // Decode the byte array into a char array and write it to the console.
        char[] charArray = new char[uniEncoding.GetCharCount(byteArray, 0, count)];
        uniEncoding.GetDecoder().GetChars(byteArray, 0, count, charArray, 0);
        Console.WriteLine(charArray);
    }

    ToArray()与GetBuffer()的区别:

    byte[] byteArray = memStream.ToArray();将流中的数据复制到一个byte[]中,速度比GetBuffer()稍慢,但不会将无用的空数据放入buffer中。
    byte[] byteArray = memStream.GetBuffer();把流中的Buffer的引用传递出来,速度较快,Buffer的大小有流的Capacity决定的,但传无用的空数据。

  • 相关阅读:
    批处理实现SQLServer数据库备份与还原
    Axapta物流模块深度历险(二)
    Axapta4.0Tech
    Script#
    Axapta物流模块深度历险(一)
    Agrs Class
    折半的意义
    个人性格
    诚实
    英语学习闪存
  • 原文地址:https://www.cnblogs.com/springsnow/p/9428715.html
Copyright © 2020-2023  润新知