求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
.NET 文件流详解:System.IO之Stream
 
发布于2013-6-20
 

Stream在msdn的定义:提供字节序列的一般性视图(provides a generic view of a sequence of bytes)。这个解释太抽象了,不容易理解;从stream的字面意思“河,水流”更容易理解些,stream是一个抽象类,它定义了类似“水流”的事物的一些统一行为,包括这个“水流”是否可以抽水出来(读取流内容);是否可以往这个“水流”中注水(向流中写入内容);以及这个“水流”有多长;如何关闭“水流”,如何向“水流”中注水,如何从“水流”中抽水等“水流”共有的行为。

常用的Stream的子类有:

1) MemoryStream 存储在内存中的字节流

2) FileStream 存储在文件系统的字节流

3) NetworkStream 通过网络设备读写的字节流

4) BufferedStream 为其他流提供缓冲的流

Stream提供了读写流的方法是以字节的形式从流中读取内容。而我们经常会用到从字节流中读取文本或者写入文本,微软提供了StreamReader和StreamWriter类帮我们实现在流上读写字符串的功能。

下面看下如何操作Stream,即如何从流中读取字节序列,如何向流中写字节

1. 使用Stream.Read方法从流中读取字节,如下示例注释:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace UseStream
{
    class Program
    {
        //示例如何从流中读取字节流
        static void Main(string[] args)
        {
            var bytes = new byte[] {(byte)1,(byte)2,(byte)3,(byte)4,(byte)5,(byte)6,(byte)7,(byte)8};
            using (var memStream = new MemoryStream(bytes))
            {
                int offset = 0;
                int readOnce = 4;
                 
                do
                {
                    byte[] byteTemp = new byte[readOnce];
                    // 使用Read方法从流中读取字节
                    //第一个参数byte[]存储从流中读出的内容
                    //第二个参数为存储到byte[]数组的开始索引,
                    //第三个int参数为一次最多读取的字节数
                    //返回值是此次读取到的字节数,此值小于等于第三个参数
                    int readCn = memStream.Read(byteTemp, 0, readOnce);
                    for (int i = 0; i < readCn; i++)
                    {
                        Console.WriteLine(byteTemp[i].ToString());
                    }
                     
                    offset += readCn;

                    //当实际读取到的字节数小于设定的读取数时表示到流的末尾了
                    if (readCn < readOnce) break;
                } while (true);
            }

            Console.Read();
        }
    }
}

2. 使用Stream.BeginRead方法读取FileStream的流内容

注意:BeginRead在一些流中的实现和Read完全相同,比如MemoryStream;而在FileStream和NetwordStream中BeginRead就是实实在在的异步操作了。

如下示例代码和注释:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

namespace UseBeginRead
{
    class Program
    {
        //定义异步读取状态类
        class AsyncState
        {
            public FileStream FS { get; set; }

            public byte[] Buffer { get; set; }

            public ManualResetEvent EvtHandle { get; set; }
        }

        static  int bufferSize = 512;

        static void Main(string[] args)
        {
            string filePath = "d:\\test.txt";
            //以只读方式打开文件流
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                var buffer = new byte[bufferSize];

                //构造BeginRead需要传递的状态
                var asyncState = new AsyncState { FS = fileStream, Buffer = buffer ,EvtHandle = new ManualResetEvent(false)};

                //异步读取
                IAsyncResult asyncResult = fileStream.BeginRead(buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState);

                //阻塞当前线程直到读取完毕发出信号
                asyncState.EvtHandle.WaitOne();
                Console.WriteLine();
                Console.WriteLine("read complete");
                Console.Read();
            }
        }

        //异步读取回调处理方法
        public static void AsyncReadCallback(IAsyncResult asyncResult)
        {
            var asyncState = (AsyncState)asyncResult.AsyncState;
            int readCn = asyncState.FS.EndRead(asyncResult);
            //判断是否读到内容
            if (readCn > 0)
            {
                byte[] buffer;
                if (readCn == bufferSize) buffer = asyncState.Buffer;
                else
                {
                    buffer = new byte[readCn];
                    Array.Copy(asyncState.Buffer, 0, buffer, 0, readCn);
                }

                //输出读取内容值
                string readContent = Encoding.UTF8.GetString(buffer);
                 
                Console.Write(readContent);
            }

            if (readCn < bufferSize)
            {
                asyncState.EvtHandle.Set();
            }
            else {
                Array.Clear(asyncState.Buffer, 0, bufferSize);
                //再次执行异步读取操作
                asyncState.FS.BeginRead(asyncState.Buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState);
            }
        }
    }
}

3. 使用Stream.Write方法向流中写字节数组

在使用Write方法时,需要先使用Stream的CanWrite方法判断流是否可写,如下示例定义了一个MemoryStream对象,然后向内存流中写入一个字节数组

using System;

  using System.Collections.Generic;

  using System.Linq;

  using System.Text;

  using System.IO;

  namespace UseStreamWrite

  {

  class Program

  {

  static void Main(string[] args)

  {

  using (var ms = new MemoryStream())

  {

  int count = 20;

  var buffer = new byte[count];

  for (int i = 0; i < count; i++)

  {

  buffer[i] = (byte)i;

  }

  //将流当前位置设置到流的起点

  ms.Seek(0, SeekOrigin.Begin);

  Console.WriteLine("ms position is " + ms.Position);

  //注意在调用Stream的Write方法之前要用CanWrite判断Stream是否可写

  if (ms.CanWrite)

  {

  ms.Write(buffer, 0, count);

  }

  //正确写入的话,流的位置会移动到写入开始位置加上写入的字节数

  Console.WriteLine("ms position is " + ms.Position);

  }

  Console.Read();

  }

  }

  }

4. 使用Stream.BeginWrite方法异步写;异步写可以提高程序性能,这是因为磁盘或者网络IO的速度远小于cpu的速度,异步写可以减少cpu的等待时间。

如下使用FileStream异步写文件的操作示例

using System;

  using System.Collections.Generic;

  using System.Linq;

  using System.Text;

  using System.IO;

  using System.Threading;

  namespace UseStreamBeginWrite

  {

  class Program

  {

  /// 

  /// 异步回调需要的参数封装类

  /// 

  class AsyncState {

  public int WriteCountOnce { get; set; }

  public int Offset { get; set; }

  public byte[] Buffer { get; set; }

  public ManualResetEvent WaitHandle { get; set; }

  public FileStream FS { get; set; }

  }

  static void Main(string[] args)

  {

  //准备一个1K的字节数组

  byte[] toWriteBytes = new byte[1 << 10];

  for (int i = 0; i < toWriteBytes.Length; i++)

  {

  toWriteBytes[i] = (byte)(i % byte.MaxValue);

  }

  string filePath = "d:\\test.txt";

  //FileStream实例

  using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))

  {

  int offset = 0;

  //每次写入32字节

  int writeCountOnce = 1 << 5;

  //构造回调函数需要的状态

  AsyncState state = new AsyncState{

  WriteCountOnce = writeCountOnce,

  Offset = offset,

  Buffer = toWriteBytes,

  WaitHandle = new ManualResetEvent(false),

  FS = fileStream

  };

  //做异步写操作

  fileStream.BeginWrite(toWriteBytes, offset, writeCountOnce, WriteCallback, state);

  //等待写完毕或者出错发出的继续信号

  state.WaitHandle.WaitOne();

  }

  Console.WriteLine("Done");

  Console.Read();

  }

  /// 

  /// 异步写的回调函数

  /// 

  /// 写状态

  static void WriteCallback(IAsyncResult asyncResult)

  {

  AsyncState state = (AsyncState)asyncResult.AsyncState;

  try

  {

  state.FS.EndWrite(asyncResult);

  }

  catch (Exception ex)

  {

  Console.WriteLine("EndWrite Error:" + ex.Message);

  state.WaitHandle.Set();

  return;

  }

  Console.WriteLine("write to " + state.FS.Position);

  //判断是否写完,未写完继续异步写

  if (state.Offset + state.WriteCountOnce < state.Buffer.Length)

  {

  state.Offset += state.WriteCountOnce;

  Console.WriteLine("call BeginWrite again");

  state.FS.BeginWrite(state.Buffer, state.Offset, state.WriteCountOnce, WriteCallback, state);

  }

  else {

  //写完发出完成信号

  state.WaitHandle.Set();

  }

  }

  }

  }
 
相关文章

需求分析师的能力模型
基于模型的需求管理方法与工具
需求管理工具DOORS 的接口
使用Web+EA实现基于模型的需求管理
需求经过大脑的过程:需求分析评估方法
 
相关文档

需求分析与需求管理
需求分析具体要求全解
需求分析与验证
需求分析的核心线索
基于UML的需求分析方法
 
相关课程

需求分析与管理
从需求过渡到设计
业务建模与业务分析
产品需求分析与管理
需求分析最佳实践与沙盘演练
 
分享到
 
 


使用decj简化Web前端开发
Web开发框架形成之旅
更有效率的使用Visual Studio
MVP+WCF+三层结构搭建框架
ASP.NET运行机制浅析【图解】
编写更好的C#代码
10个Visual Studio开发调试技巧
更多...   


.NET框架与分布式应用架构设计
.NET & WPF & WCF应用开发
UML&.Net架构设计
COM组件开发
.Net应用开发
InstallShield


日照港 .NET Framework & WCF应用开发
神华信息 .NET单元测试
北京 .Net应用软件系统架构
台达电子 .NET程序设计与开发
赛门铁克 C#与.NET架构设计
广东核电 .Net应用系统架构
更多...