.Net筆記:System.IO之Stream的使用詳解
更新時間:2013年05月18日 16:26:29 作者:
本篇文章是對.Net中System.IO之Stream的使用進行了詳細的分析介紹,需要的朋友參考下
Stream在msdn的定義:提供字節(jié)序列的一般性視圖(provides a generic view of a sequence of bytes)。這個解釋太抽象了,不容易理解;從stream的字面意思“河,水流”更容易理解些,stream是一個抽象類,它定義了類似“水流”的事物的一些統(tǒng)一行為,包括這個“水流”是否可以抽水出來(讀取流內(nèi)容);是否可以往這個“水流”中注水(向流中寫入內(nèi)容);以及這個“水流”有多長;如何關(guān)閉“水流”,如何向“水流”中注水,如何從“水流”中抽水等“水流”共有的行為。
常用的Stream的子類有:
1) MemoryStream 存儲在內(nèi)存中的字節(jié)流
2) FileStream 存儲在文件系統(tǒng)的字節(jié)流
3) NetworkStream 通過網(wǎng)絡(luò)設(shè)備讀寫的字節(jié)流
4) BufferedStream 為其他流提供緩沖的流
Stream提供了讀寫流的方法是以字節(jié)的形式從流中讀取內(nèi)容。而我們經(jīng)常會用到從字節(jié)流中讀取文本或者寫入文本,微軟提供了StreamReader和StreamWriter類幫我們實現(xiàn)在流上讀寫字符串的功能。
下面看下如何操作Stream,即如何從流中讀取字節(jié)序列,如何向流中寫字節(jié)
1. 使用Stream.Read方法從流中讀取字節(jié),如下示例注釋:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace UseStream
{
class Program
{
//示例如何從流中讀取字節(jié)流
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方法從流中讀取字節(jié)
//第一個參數(shù)byte[]存儲從流中讀出的內(nèi)容
//第二個參數(shù)為存儲到byte[]數(shù)組的開始索引,
//第三個int參數(shù)為一次最多讀取的字節(jié)數(shù)
//返回值是此次讀取到的字節(jié)數(shù),此值小于等于第三個參數(shù)
int readCn = memStream.Read(byteTemp, 0, readOnce);
for (int i = 0; i < readCn; i++)
{
Console.WriteLine(byteTemp[i].ToString());
}
offset += readCn;
//當實際讀取到的字節(jié)數(shù)小于設(shè)定的讀取數(shù)時表示到流的末尾了
if (readCn < readOnce) break;
} while (true);
}
Console.Read();
}
}
}
2. 使用Stream.BeginRead方法讀取FileStream的流內(nèi)容
注意:BeginRead在一些流中的實現(xiàn)和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
{
//定義異步讀取狀態(tài)類
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];
//構(gòu)造BeginRead需要傳遞的狀態(tài)
var asyncState = new AsyncState { FS = fileStream, Buffer = buffer ,EvtHandle = new ManualResetEvent(false)};
//異步讀取
IAsyncResult asyncResult = fileStream.BeginRead(buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState);
//阻塞當前線程直到讀取完畢發(fā)出信號
asyncState.EvtHandle.WaitOne();
Console.WriteLine();
Console.WriteLine("read complete");
Console.Read();
}
}
//異步讀取回調(diào)處理方法
public static void AsyncReadCallback(IAsyncResult asyncResult)
{
var asyncState = (AsyncState)asyncResult.AsyncState;
int readCn = asyncState.FS.EndRead(asyncResult);
//判斷是否讀到內(nèi)容
if (readCn > 0)
{
byte[] buffer;
if (readCn == bufferSize) buffer = asyncState.Buffer;
else
{
buffer = new byte[readCn];
Array.Copy(asyncState.Buffer, 0, buffer, 0, readCn);
}
//輸出讀取內(nèi)容值
string readContent = Encoding.UTF8.GetString(buffer);
Console.Write(readContent);
}
if (readCn < bufferSize)
{
asyncState.EvtHandle.Set();
}
else {
Array.Clear(asyncState.Buffer, 0, bufferSize);
//再次執(zhí)行異步讀取操作
asyncState.FS.BeginRead(asyncState.Buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState);
}
}
}
}
3. 使用Stream.Write方法向流中寫字節(jié)數(shù)組
在使用Write方法時,需要先使用Stream的CanWrite方法判斷流是否可寫,如下示例定義了一個MemoryStream對象,然后向內(nèi)存流中寫入一個字節(jié)數(shù)組
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;
}
//將流當前位置設(shè)置到流的起點
ms.Seek(0, SeekOrigin.Begin);
Console.WriteLine("ms position is " + ms.Position);
//注意在調(diào)用Stream的Write方法之前要用CanWrite判斷Stream是否可寫
if (ms.CanWrite)
{
ms.Write(buffer, 0, count);
}
//正確寫入的話,流的位置會移動到寫入開始位置加上寫入的字節(jié)數(shù)
Console.WriteLine("ms position is " + ms.Position);
}
Console.Read();
}
}
}
4. 使用Stream.BeginWrite方法異步寫;異步寫可以提高程序性能,這是因為磁盤或者網(wǎng)絡(luò)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>
/// 異步回調(diào)需要的參數(shù)封裝類
/// </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的字節(jié)數(shù)組
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字節(jié)
int writeCountOnce = 1 << 5;
//構(gòu)造回調(diào)函數(shù)需要的狀態(tài)
AsyncState state = new AsyncState{
WriteCountOnce = writeCountOnce,
Offset = offset,
Buffer = toWriteBytes,
WaitHandle = new ManualResetEvent(false),
FS = fileStream
};
//做異步寫操作
fileStream.BeginWrite(toWriteBytes, offset, writeCountOnce, WriteCallback, state);
//等待寫完畢或者出錯發(fā)出的繼續(xù)信號
state.WaitHandle.WaitOne();
}
Console.WriteLine("Done");
Console.Read();
}
/// <summary>
/// 異步寫的回調(diào)函數(shù)
/// </summary>
/// <param name="asyncResult">寫狀態(tài)</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);
//判斷是否寫完,未寫完繼續(xù)異步寫
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 {
//寫完發(fā)出完成信號
state.WaitHandle.Set();
}
}
}
}
常用的Stream的子類有:
1) MemoryStream 存儲在內(nèi)存中的字節(jié)流
2) FileStream 存儲在文件系統(tǒng)的字節(jié)流
3) NetworkStream 通過網(wǎng)絡(luò)設(shè)備讀寫的字節(jié)流
4) BufferedStream 為其他流提供緩沖的流
Stream提供了讀寫流的方法是以字節(jié)的形式從流中讀取內(nèi)容。而我們經(jīng)常會用到從字節(jié)流中讀取文本或者寫入文本,微軟提供了StreamReader和StreamWriter類幫我們實現(xiàn)在流上讀寫字符串的功能。
下面看下如何操作Stream,即如何從流中讀取字節(jié)序列,如何向流中寫字節(jié)
1. 使用Stream.Read方法從流中讀取字節(jié),如下示例注釋:
復制代碼 代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace UseStream
{
class Program
{
//示例如何從流中讀取字節(jié)流
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方法從流中讀取字節(jié)
//第一個參數(shù)byte[]存儲從流中讀出的內(nèi)容
//第二個參數(shù)為存儲到byte[]數(shù)組的開始索引,
//第三個int參數(shù)為一次最多讀取的字節(jié)數(shù)
//返回值是此次讀取到的字節(jié)數(shù),此值小于等于第三個參數(shù)
int readCn = memStream.Read(byteTemp, 0, readOnce);
for (int i = 0; i < readCn; i++)
{
Console.WriteLine(byteTemp[i].ToString());
}
offset += readCn;
//當實際讀取到的字節(jié)數(shù)小于設(shè)定的讀取數(shù)時表示到流的末尾了
if (readCn < readOnce) break;
} while (true);
}
Console.Read();
}
}
}
2. 使用Stream.BeginRead方法讀取FileStream的流內(nèi)容
注意:BeginRead在一些流中的實現(xiàn)和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
{
//定義異步讀取狀態(tài)類
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];
//構(gòu)造BeginRead需要傳遞的狀態(tài)
var asyncState = new AsyncState { FS = fileStream, Buffer = buffer ,EvtHandle = new ManualResetEvent(false)};
//異步讀取
IAsyncResult asyncResult = fileStream.BeginRead(buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState);
//阻塞當前線程直到讀取完畢發(fā)出信號
asyncState.EvtHandle.WaitOne();
Console.WriteLine();
Console.WriteLine("read complete");
Console.Read();
}
}
//異步讀取回調(diào)處理方法
public static void AsyncReadCallback(IAsyncResult asyncResult)
{
var asyncState = (AsyncState)asyncResult.AsyncState;
int readCn = asyncState.FS.EndRead(asyncResult);
//判斷是否讀到內(nèi)容
if (readCn > 0)
{
byte[] buffer;
if (readCn == bufferSize) buffer = asyncState.Buffer;
else
{
buffer = new byte[readCn];
Array.Copy(asyncState.Buffer, 0, buffer, 0, readCn);
}
//輸出讀取內(nèi)容值
string readContent = Encoding.UTF8.GetString(buffer);
Console.Write(readContent);
}
if (readCn < bufferSize)
{
asyncState.EvtHandle.Set();
}
else {
Array.Clear(asyncState.Buffer, 0, bufferSize);
//再次執(zhí)行異步讀取操作
asyncState.FS.BeginRead(asyncState.Buffer, 0, bufferSize, new AsyncCallback(AsyncReadCallback), asyncState);
}
}
}
}
3. 使用Stream.Write方法向流中寫字節(jié)數(shù)組
在使用Write方法時,需要先使用Stream的CanWrite方法判斷流是否可寫,如下示例定義了一個MemoryStream對象,然后向內(nèi)存流中寫入一個字節(jié)數(shù)組
復制代碼 代碼如下:
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;
}
//將流當前位置設(shè)置到流的起點
ms.Seek(0, SeekOrigin.Begin);
Console.WriteLine("ms position is " + ms.Position);
//注意在調(diào)用Stream的Write方法之前要用CanWrite判斷Stream是否可寫
if (ms.CanWrite)
{
ms.Write(buffer, 0, count);
}
//正確寫入的話,流的位置會移動到寫入開始位置加上寫入的字節(jié)數(shù)
Console.WriteLine("ms position is " + ms.Position);
}
Console.Read();
}
}
}
4. 使用Stream.BeginWrite方法異步寫;異步寫可以提高程序性能,這是因為磁盤或者網(wǎng)絡(luò)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>
/// 異步回調(diào)需要的參數(shù)封裝類
/// </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的字節(jié)數(shù)組
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字節(jié)
int writeCountOnce = 1 << 5;
//構(gòu)造回調(diào)函數(shù)需要的狀態(tài)
AsyncState state = new AsyncState{
WriteCountOnce = writeCountOnce,
Offset = offset,
Buffer = toWriteBytes,
WaitHandle = new ManualResetEvent(false),
FS = fileStream
};
//做異步寫操作
fileStream.BeginWrite(toWriteBytes, offset, writeCountOnce, WriteCallback, state);
//等待寫完畢或者出錯發(fā)出的繼續(xù)信號
state.WaitHandle.WaitOne();
}
Console.WriteLine("Done");
Console.Read();
}
/// <summary>
/// 異步寫的回調(diào)函數(shù)
/// </summary>
/// <param name="asyncResult">寫狀態(tài)</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);
//判斷是否寫完,未寫完繼續(xù)異步寫
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 {
//寫完發(fā)出完成信號
state.WaitHandle.Set();
}
}
}
}