本篇文章是對.Net中System.IO之Stream的使用進行了詳細的分析介紹,需要的朋友參考下
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
{
/// <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();
}
}
}
}