.Net那点事儿系列:System.IO之Stream

来源:岁月联盟 编辑:exp 时间:2011-07-28

 

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方法从流中读取字节,如下示例注释:

 

view sourceprint?01 using System; 

 

02 using System.Collections.Generic; 

 

03 using System.Linq; 

 

04 using System.Text; 

 

05 using System.IO; 

 

06   

 

07 namespace UseStream 

 

08 { 

 

09     class Program 

 

10     { 

 

11         //示例如何从流中读取字节流 

 

12         static void Main(string[] args) 

 

13         { 

 

14             var bytes = new byte[] {(byte)1,(byte)2,(byte)3,(byte)4,(byte)5,(byte)6,(byte)7,(byte)8}; 

 

15             using (var memStream = new MemoryStream(bytes)) 

 

16             { 

 

17                 int offset = 0; 

 

18                 int readOnce = 4; 

 

19                   

 

20                 do

 

21                 { 

 

22                     byte[] byteTemp = new byte[readOnce]; 

 

23                     // 使用Read方法从流中读取字节 

 

24                     //第一个参数byte[]存储从流中读出的内容 

 

25                     //第二个参数为存储到byte[]数组的开始索引, 

 

26                     //第三个int参数为一次最多读取的字节数  

 

27                     //返回值是此次读取到的字节数,此值小于等于第三个参数 

 

28                     int readCn = memStream.Read(byteTemp, 0, readOnce); 

 

29                     for (int i = 0; i < readCn; i++) 

 

30                     { 

 

31                         Console.WriteLine(byteTemp[i].ToString()); 

 

32                     } 

 

33                       

 

34                     offset += readCn; 

 

35   

 

36                     //当实际读取到的字节数小于设定的读取数时表示到流的末尾了 

 

37                     if (readCn < readOnce) break; 

 

38                 } while (true); 

 

39             } 

 

40   

 

41             Console.Read(); 

 

42         } 

 

43     } 

 

44 }

 

 

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

 

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

 

如下示例代码和注释:

 

view sourceprint?01 using System; 

 

02 using System.Collections.Generic; 

 

03 using System.Linq; 

 

04 using System.Text; 

 

05 using System.IO; 

 

06 using System.Threading; 

 

07   

 

08 namespace UseBeginRead 

 

09 { 

 

10     class Program 

 

11     { 

 

12         //定义异步读取状态类 

 

13         class AsyncState 

 

14         { 

 

15             public FileStream FS { get; set; } 

 

16   

 

17             public byte[] Buffer { get; set; } 

 

18   

 

19             public ManualResetEvent EvtHandle { get; set; } 

 

20         } 

 

21   

 

22         static  int bufferSize = 512; 

 

23   

 

24         static void Main(string[] args) 

 

25         { 

 

26             string filePath = "d://test.txt"; 

 

27             //以只读方式打开文件流 

 

28             using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) 

 

29             { 

 

30                 var buffer = new byte[bufferSize]; 

 

31   

 

32                 //构造BeginRead需要传递的状态 

 

33                 var asyncState = new AsyncState { FS = fileStream, Buffer = buffer ,EvtHandle = new ManualResetEvent(false)}; 

 

34   

 

35                 //异步读取 

 

36                 IAsyncResult asyncResult = fileStream.BeginRead(buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState); 

 

37   

 

38                 //阻塞当前线程直到读取完毕发出信号 

 

39                 asyncState.EvtHandle.WaitOne(); 

 

40                 Console.WriteLine(); 

 

41                 Console.WriteLine("read complete"); 

 

42                 Console.Read(); 

 

43             } 

 

44         } 

 

45   

 

46         //异步读取回调处理方法 

 

47         public static void AsyncReadCallback(IAsyncResult asyncResult) 

 

48         { 

 

49             var asyncState = (AsyncState)asyncResult.AsyncState; 

 

50             int readCn = asyncState.FS.EndRead(asyncResult); 

 

51             //判断是否读到内容 

 

52             if (readCn > 0) 

 

53             { 

 

54                 byte[] buffer; 

 

55                 if (readCn == bufferSize) buffer = asyncState.Buffer; 

 

56                 else

 

57                 { 

 

58                     buffer = new byte[readCn]; 

 

59                     Array.Copy(asyncState.Buffer, 0, buffer, 0, readCn); 

 

60                 } 

 

61   

 

62                 //输出读取内容值 

 

63                 string readContent = Encoding.UTF8.GetString(buffer); 

 

64                   

 

65                 Console.Write(readContent); 

 

66             } 

 

67   

 

68             if (readCn < bufferSize) 

 

69             { 

 

70                 asyncState.EvtHandle.Set(); 

 

71             } 

 

72             else { 

 

73                 Array.Clear(asyncState.Buffer, 0, bufferSize); 

 

74                 //再次执行异步读取操作 

 

75                 asyncState.FS.BeginRead(asyncState.Buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState); 

 

76             } 

 

77         } 

 

78     } 

 

79 }

 

 

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

 

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

 

view sourceprint?01 using System; 

 

02 using System.Collections.Generic; 

 

03 using System.Linq; 

 

04 using System.Text; 

 

05 using System.IO; 

 

06   

 

07 namespace UseStreamWrite 

 

08 { 

 

09     class Program 

 

10     { 

 

11         static void Main(string[] args) 

 

12         { 

 

13             using (var ms = new MemoryStream()) 

 

14             { 

 

15                 int count = 20; 

 

16                 var buffer = new byte[count]; 

 

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

 

18                 { 

 

19                     buffer[i] = (byte)i; 

 

20                 } 

 

21   

 

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

 

23                 ms.Seek(0, SeekOrigin.Begin); 

 

24   

 

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

 

26    

 

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

 

28                 if (ms.CanWrite) 

 

29                 { 

 

30                     ms.Write(buffer, 0, count); 

 

31                 } 

 

32   

 

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

 

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

 

35   

 

36             } 

 

37   

 

38             Console.Read(); 

 

39         } 

 

40     } 

 

41 } 

 

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

 

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

 

view sourceprint?using System; 

 

using System.Collections.Generic; 

 

using System.Linq; 

 

using System.Text; 

 

using System.IO; 

 

using System.Threading; 

 

  

 

namespace UseStreamBeginWrite 

 

 

    class Program 

 

    { 

 

        /// <summary> 

 

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

 

        /// </summary> 

 

        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(); 

 

        } 

 

  

 

        /// <summary> 

 

        /// 异步写的回调函数 

 

        /// </summary> 

 

        /// <param name="asyncResult">写状态</param> 

 

        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(); 

 

            } 

 

        } 

 

    } 

 

}