审查征集贴:http://www.cnblogs.com/BeginnerClassroom/archive/2010/07/30/1788649.html

附录征集贴:http://www.cnblogs.com/BeginnerClassroom/archive/2010/08/04/1792175.html

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

要求:

  1. 紧紧围绕一两个中心展开;
  2. 逻辑清晰,行文流畅;
  3. 考虑到初学者的基础。
  4. 写作时间最好不要少于一星期。

我写东西都是写好以后先放在那里,过段时间再读,重新修改,如此反复几次,就基本上很流畅了。

 

(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