《叩響C#之門》園友提供的附錄(徵集中,歡迎提意見)讀取流時應注意的一個問題

來源:互聯網
上載者:User

審查徵集貼: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版本變更表 張智鳴 正在寫
字元編碼 趙士敬 正在寫
讀取流時應注意的一個問題 黃志斌 正在寫
Regex在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,904BinaryReader.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,356BinaryReader.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,904BinaryReader.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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.