|
Microsoft .NET Framework Base Class Library 中的 Stream.Read 方法:
請(qǐng)注意上述的 MSDN 中的最后一句話。我們寫一個(gè)程序來(lái)驗(yàn)證這一點(diǎn):
using System;using System.IO;using Skyiv.Util;namespace Skyiv.Ben.StreamTest{ sealed class Program { static void Main() { var bs = new byte[128 * 1024]; var stream = new FtpClient("ftp://ftp.hp.com", "anonymous", "ben@skyiv.com"). GetDownloadStream("pub/softpaq/allfiles.txt"); // 568,320 bytes var br = new BinaryReader(stream); Display("Expect", bs.Length); Display("Stream.Read", stream.Read(bs, 0, bs.Length)); Display("BinaryReader.Read", br.Read(bs, 0, bs.Length)); Display("BinaryReader.ReadBytes", br.ReadBytes(bs.Length).Length); Display("Stream.Readbytes", stream.ReadBytes(bs.Length).Length); } static void Display(string msg, int n) { Console.WriteLine("{0,22}: {1,7:N0}", msg, n); } }}
將這個(gè)程序運(yùn)行三次的結(jié)果如下:
可見(jiàn),Stream.Read 方法和 BinaryReader.Read 方法在尚未到達(dá)流的末尾情況下可以返回少于所請(qǐng)求的字節(jié)。
通過(guò)使用 Reflector 來(lái)查看 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("ArgumentOutOfRan
ge_NeedNonNegNum")); } if (count < 0) { throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRa
nge_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 類的基礎(chǔ)流。可見(jiàn),BinaryReader.Read 方法在做一些必要的檢查后就是簡(jiǎn)單地調(diào)用 Stream.Read 方法。
而 BinaryReader.ReadBytes 方法的源程序代碼如下:
public virtual byte[] ReadBytes(int count){ if (count < 0) { throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRan
ge_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 方法循環(huán)地調(diào)用 Stream.Read 方法,直到達(dá)到流的末尾,或者已經(jīng)讀取了 count 個(gè)字節(jié)。也就是說(shuō),如果沒(méi)有到達(dá)流的末尾,該方法就一定會(huì)返回所請(qǐng)求的字節(jié)。
MSDN 文檔中對(duì)這兩個(gè)方法的描述:
也就是說(shuō),雖然 BinaryReader.Read 方法和 Stream.Read 方法一樣在尚未到達(dá)流的末尾情況下可以返回少于所請(qǐng)求的字節(jié),但是在 MSDN 文檔中并沒(méi)有指出這一點(diǎn),我們寫程序的時(shí)候要小心,避免掉入這個(gè)陷阱。
上述的測(cè)試程序中用到了 Stream.ReadBytes 方法,其實(shí)是一個(gè)擴(kuò)展方法,源程序代碼如下:
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", "要求非負(fù)數(shù)"); 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; } }}
上述的測(cè)試程序中還使用了 FtpClient 類,可以參見(jiàn)我的另一篇隨筆“如何直接處理FTP服務(wù)器上的壓縮文件”,其源程序代碼如下:
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(); } }}
我在上一篇隨筆“【算法】利用有限自動(dòng)機(jī)進(jìn)行字符串匹配”中給出了一道思考題如下:
上述思考題中的第二個(gè) C# 程序的 Main 方法如下所示:
static void Main(){ var s = new byte[10000000 + 2 * 1000 + 100]; int i = 0, n = Console.OpenStandardInput().Read(s, 0, s.Length); while (s[i++] != '/n') ; for (int c, q = 0; i < n; q = 0) { while ((c = s[i++]) != '/n') if (q < 99 && c != '/r') q = delta[q, Array.IndexOf(a, c) + 1]; Console.WriteLine((q < 4) ? "YES" : "NO"); }}
這個(gè) bug 至今還沒(méi)有人找到。實(shí)際上,該方法的頭兩個(gè)語(yǔ)句應(yīng)改為:
var s = new BinaryReader(Console.OpenStandardInput()).ReadBytes(10000000 + 2 * 1000 + 100);int i = 0, n = s.Length;
這是因?yàn)?Steam.Read 方法在尚未到達(dá)流的末尾情況下可以返回少于所請(qǐng)求的字節(jié),這有可能導(dǎo)致只讀取了部分輸入而產(chǎn)生 bug 。
NET技術(shù):淺談 Stream.Read 方法,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。