转自 :http://www.cnblogs.com/yukaizhao/archive/2011/07/28/stream.html
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方法从流中读取字节,如下示例注释:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
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就是实实在在的异步操作了。
如下示例代码和注释:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
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对象,然后向内存流中写入一个字节数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
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();
}
}
}
}
|