您的位置:首页 > 编程语言 > C#

《叩响C#之门》园友提供的附录(征集中,欢迎提意见)读取流时应注意的一个问题

2010-08-11 14:24 405 查看
审查征集贴:/article/4891417.html

附录征集贴:/article/4891419.html

欢迎各位园友对本书的某一部分内容进行拓展,将以附录的形式附在书后。

要求:

紧紧围绕一两个中心展开; 逻辑清晰,行文流畅; 考虑到初学者的基础。 写作时间最好不要少于一星期。
我写东西都是写好以后先放在那里,过段时间再读,重新修改,如此反复几次,就基本上很流畅了。

(PS:会署名,但无稿费,因为本来就没多少,不够分的。当然如果发了大财,我会分给大家的。)

标题作者状态
关于RichTextBox修改字体大小的研究李雨来已完稿
委托和接口的区别汤非凡正在写
XML格式注释Capricornus 正在写
接口的显式实现以及与抽象类的比较顾磊正在写
.NET版本变更表张智鸣正在写
字符编码赵士敬正在写
读取流时应注意的一个问题黄志斌正在写
正则表达式在EmEditor里的应用柳永法正在写
绘图缓存待选
异步读写操作待选
控件开发、自定义控件MingHao_Hu正在写

读取流时应注意的一个问题

(本文由黄志斌提供) Stream 类是所有流的抽象基类,通过它及它的子类,使程序员不必了解操作系统和基础设备的具体细节,即可对流进行“读取”、“写入”、“查询”等操作。希望本文的例子能帮助你掌握流的用法。
下面来研究一下Stream 类及其派生类的读取数据的成员。

Stream.Read()
Stream.ReadBytes()
BinaryReader.Read()
BinaryReader.ReadBytes()
TextReader.Read()
TextReader.ReadBlock ()
Stream.Read方法用于从流中读取字节序列,并将流的当前位置提升相应的字节数。在 MSDN 中有这样一句话:“即使尚未到达流的末尾,该方法获取到的字节数仍可以能少于所请求的字节数。”现在我们写一个程序来验证这一点。
using System;
using System.IO;
using Skyiv.Util;
namespace Skyiv.Ben.StreamTest
{
sealed class Program
{
static void Display(string msg, int n)
{
Console.WriteLine("{0,22}: {1,7:N0}", msg, n);
}

static void Main()
{
var bs = new byte[128 * 1024];    //131,072
var ftp = new FtpClient("ftp://ftp.hp.com", "anonymous", "ben@skyiv.com");

Stream stream = ftp.GetDownloadStream("pub/softpaq/allfiles.txt");
BinaryReader binaryReader = new BinaryReader(stream);
TextReader textReader = new StreamReader(stream);

int count1 = stream.Read(bs, 0, bs.Length);
int count2 = stream.ReadBytes(bs.Length).Length;
int count3 = binaryReader.Read(bs, 0, bs.Length);
int count4 = binaryReader.ReadBytes(bs.Length).Length;
int count5 = textReader.Read(buf, 0, buf.Length);
int count6 = textReader.ReadBlock(buf, 0, buf.Length);

Display("Expect", bs.Length);
Display("Stream.Read", count1);
Display("Stream.ReadBytes", count2);
Display("BinaryReader.Read", count3);
Display("BinaryReader.ReadBytes", count4);
Display("TextReader.Read", count5);
Display("TextReader.ReadBlock", count6);

}
}
}


======

将这个程序运行三次的结果如下:
Expect: 131,072
Stream.Read:   4,356
Stream.ReadBytes: 131,072
BinaryReader.Read:   2,904
BinaryReader.ReadBytes: 131,072
TextReader.Read: 123,812
TextReader.ReadBlock: 131,072

Expect: 131,072
Stream.Read:   4,356
Stream.ReadBytes: 131,072
BinaryReader.Read:   4,356
BinaryReader.ReadBytes: 131,072
TextReader.Read:   2,904
TextReader.ReadBlock: 131,072

Expect: 131,072
Stream.Read:   4,356
Stream.ReadBytes: 131,072
BinaryReader.Read:   2,904
BinaryReader.ReadBytes: 131,072
TextReader.Read:   5,808
TextReader.ReadBlock: 131,072



可见,Stream.Read()、BinaryReader.Read()和TextReader.Read()方法,在尚未到达流的末尾情况下,获取到的字节数仍可以能少于所请求的字节数,这种问题在处理网络流(如FTP)、设备流(如串口输入)等情况时经常发生,而Stream.ReadBytes()、BinaryReader.ReadBytes()、和TextReader.ReadBlock()方法则无此问题。
现在,我们通过 Reflector 来查看BinaryReader.Read()方法的源程序代码。
public virtual int Read(byte[] buffer, int index, int count)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
}
if (index < 0)
{
throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
}
if ((buffer.Length - index) < count)
{
throw new ArgumentException(Environment.GetResourceString(
"Argument_InvalidOffLen"));
}
if (this.m_stream == null)
{
__Error.FileNotOpen();
}
return this.m_stream.Read(buffer, index, count);
}

======

最后一行的m_stream的类型为 Stream,可见,BinaryReader.Read()方法在做一些必要的检查后就是简单地调用Stream.Read()方法,所以它们具有相同的问题。
而 BinaryReader.ReadBytes方法的源程序代码如下:
public virtual byte[] ReadBytes(int count)
{
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", Environment. GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
}
if (this.m_stream == null)
{
__Error.FileNotOpen();
}
byte[] buffer = new byte[count];
int offset = 0;
do
{
int num2 = this.m_stream.Read(buffer, offset, count);
if (num2 == 0)
{
break;
}
offset += num2;
count -= num2;
}
while (count > 0);
if (offset != buffer.Length)
{
byte[] dst = new byte[offset];
Buffer.InternalBlockCopy(buffer, 0, dst, 0, offset);
buffer = dst;
}
return buffer;
}

======

从上述代码中可以看出,BinaryReader.ReadBytes 方法循环地调用 Stream.Read 方法,直到达到流的末尾,或者已经读取了请求的 个字节为止。也就是说,如果没有到达流的末尾,该方法就一定会返回所请求的字节。
Stream.ReadBytes()方法其实是我写的一个扩展方法,源程序代码如下:
using System;
using System.IO;

namespace Skyiv.Util
{
static class ExtensionMethods
{
public static byte[] ReadBytes(this Stream stream, int count)
{
if(count < 0) throw new ArgumentOutOfRangeException("count","??????????");
var bs = new byte[count];
var offset = 0;
for (int n = -1; n != 0 && count > 0; count -= n, offset += n)
n = stream.Read(bs, offset, count);
if (offset != bs.Length) Array.Resize(ref bs, offset);
return bs;
}
}
}

======

测试程序中使用的 FtpClient 类是我编写的类,可以参见我的另一篇随笔“如何直接处理FTP服务器上的压缩文件”[①],其源程序代码如下:
using System;
using System.IO;
using System.Net;
namespace Skyiv.Util
{
sealed class FtpClient
{
Uri uri;
string userName;
string password;

public FtpClient(string uri, string userName, string password)
{
this.uri = new Uri(uri);
this.userName = userName;
this.password = password;
}

public Stream GetDownloadStream(string sourceFile)
{
Uri downloadUri = new Uri(uri, sourceFile);
if (downloadUri.Scheme != Uri.UriSchemeFtp)
throw new ArgumentException("URI is not an FTP site");
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(downloadUri);
ftpRequest.Credentials = new NetworkCredential(userName, password);
ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile;
return ((FtpWebResponse)ftpRequest.GetResponse()).GetResponseStream();
}
}
}

======

[①] 地址为:http://www.cnblogs.com/skyivben/archive/2005/09/17/238920.html

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: