天天看点

艾伟:浅谈 Stream.Read 方法

Stream.Read 方法

当在派生类中重写时,从当前流读取字节序列,并将此流中的位置提升读取的字节数。

语法:

public abstract int Read(byte[] buffer, int offset, int count)

参数:

buffer: 字节数组。此方法返回时,该缓冲区包含指定的字符数组,该数组的 offset 和 (offset + count -1) 之间的值由从当前源中读取的字节替换。

offset: buffer 中的从零开始的字节偏移量,从此处开始存储从当前流中读取的数据。

count: 要从当前流中最多读取的字节数。

返回值:

读入缓冲区中的总字节数。如果当前可用的字节数没有请求的字节数那么多,则总字节数可能小于请求的字节数,或者如果已到达流的末尾,则为零 (0)。

备注:

此方法的实现从当前流中读取最多的 count 个字节,并将它们存储在从 offset 开始的 buffer 中。流中的当前位置提升已读取的字节数;但是,如果出现异常,流中的当前位置保持不变。实现返回已读取的字节数。仅当位置当前位于流的末尾时,返回值才为零。如果没有任何可用的数据,该实现将一直阻塞到至少有一个字节的数据可读为止。仅当流中不再有其他的数据,而且也不再需要更多的数据(如已关闭的套接字或文件尾)时,Read 才返回 0。即使尚未到达流的末尾,实现仍可以随意返回少于所请求的字节。

请注意上述的 MSDN 中的最后一句话。我们写一个程序来验证这一点:

将这个程序运行三次的结果如下:

上述代码最后一行中 m_stream 的类型为 Stream,就是 BinaryReader 类的基础流。可见,BinaryReader.Read 方法在做一些必要的检查后就是简单地调用 Stream.Read 方法。

从上述代码中可以看出,BinaryReader.ReadBytes 方法循环地调用 Stream.Read 方法,直到达到流的末尾,或者已经读取了 count 个字节。也就是说,如果没有到达流的末尾,该方法就一定会返回所请求的字节。

MSDN 文档中对这两个方法的描述:

也就是说,虽然 BinaryReader.Read 方法和 Stream.Read 方法一样在尚未到达流的末尾情况下可以返回少于所请求的字节,但是在 MSDN 文档中并没有指出这一点,我们写程序的时候要小心,避免掉入这个陷阱。

上述的测试程序中用到了 Stream.ReadBytes 方法,其实是一个扩展方法,源程序代码如下:

上面的第二个 C# 程序中有一个 bug,但是这个 bug 在绝大多数情况下都不会表现出来。所以这个程序能够 Accepted。

亲爱的读者,你能够找出这个 bug 吗?

提示,这个 bug 和字符串匹配算法无关,并且第一个 C# 程序中不存在这个 bug 。

上述思考题中的第二个 C# 程序的 Main 方法如下所示:

<a href="http://11011.net/software/vspaste"></a>

这个 bug 至今还没有人找到。实际上,该方法的头两个语句应改为:

这是因为 Steam.Read 方法在尚未到达流的末尾情况下可以返回少于所请求的字节,这有可能导致只读取了部分输入而产生 bug 。