淺談.NET Framework中的Stream.Read方法
Stream.Read 方法
當(dāng)在派生類(lèi)中重寫(xiě)時(shí),從當(dāng)前流讀取字節(jié)序列,并將此流中的位置提升讀取的字節(jié)數(shù)。
語(yǔ)法:
public abstract int Read(byte[] buffer, int offset, int count) |
參數(shù):
buffer: 字節(jié)數(shù)組。此方法返回時(shí),該緩沖區(qū)包含指定的字符數(shù)組,該數(shù)組的 offset 和 (offset + count -1) 之間的值由從當(dāng)前源中讀取的字節(jié)替換。
offset: buffer 中的從零開(kāi)始的字節(jié)偏移量,從此處開(kāi)始存儲(chǔ)從當(dāng)前流中讀取的數(shù)據(jù)。
count: 要從當(dāng)前流中最多讀取的字節(jié)數(shù)。
返回值:
讀入緩沖區(qū)中的總字節(jié)數(shù)。如果當(dāng)前可用的字節(jié)數(shù)沒(méi)有請(qǐng)求的字節(jié)數(shù)那么多,則總字節(jié)數(shù)可能小于請(qǐng)求的字節(jié)數(shù),或者如果已到達(dá)流的末尾,則為零 (0)。
備注:
此方法的實(shí)現(xiàn)從當(dāng)前流中讀取最多的 count 個(gè)字節(jié),并將它們存儲(chǔ)在從 offset 開(kāi)始的 buffer 中。流中的當(dāng)前位置提升已讀取的字節(jié)數(shù);但是,如果出現(xiàn)異常,流中的當(dāng)前位置保持不變。實(shí)現(xiàn)返回已讀取的字節(jié)數(shù)。僅當(dāng)位置當(dāng)前位于流的末尾時(shí),返回值才為零。如果沒(méi)有任何可用的數(shù)據(jù),該實(shí)現(xiàn)將一直阻塞到至少有一個(gè)字節(jié)的數(shù)據(jù)可讀為止。僅當(dāng)流中不再有其他的數(shù)據(jù),而且也不再需要更多的數(shù)據(jù)(如已關(guān)閉的套接字或文件尾)時(shí),Read 才返回 0。即使尚未到達(dá)流的末尾,實(shí)現(xiàn)仍可以隨意返回少于所請(qǐng)求的字節(jié)。
請(qǐng)注意上述的 MSDN 中的最后一句話(huà)。我們寫(xiě)一個(gè)程序來(lái)驗(yàn)證這一點(diǎn):
using System; |
將這個(gè)程序運(yùn)行三次的結(jié)果如下:
Expect: 131,072 |
可見(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) ("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); ("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); ("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); (Environment.GetResourceString("Argument_InvalidOffLen")); |
上述代碼最后一行中 m_stream 的類(lèi)型為 Stream,就是 BinaryReader 類(lèi)的基礎(chǔ)流??梢?jiàn),BinaryReader.Read 方法在做一些必要的檢查后就是簡(jiǎn)單地調(diào)用 Stream.Read 方法。
而 BinaryReader.ReadBytes 方法的源程序代碼如下:
public virtual byte[] ReadBytes(int count) ("count", Environment.GetResourceString ("ArgumentOutOfRange_NeedNonNegNum")); |
從上述代碼中可以看出,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è)方法的描述:
BinaryReader.Read 方法:將 index 作為字節(jié)數(shù)組中的起始點(diǎn),從流中讀取 count 個(gè)字節(jié)。
BinaryReader.ReadBytes 方法:從當(dāng)前流中將 count 個(gè)字節(jié)讀入字節(jié)數(shù)組,并使當(dāng)前位置提升 count 個(gè)字節(jié)。
上述兩個(gè)方法的備注: BinaryReader 在讀取失敗后不還原文件位置。
也就是說(shuō),雖然 BinaryReader.Read 方法和 Stream.Read 方法一樣在尚未到達(dá)流的末尾情況下可以返回少于所請(qǐng)求的字節(jié),但是在 MSDN 文檔中并沒(méi)有指出這一點(diǎn),我們寫(xiě)程序的時(shí)候要小心,避免掉入這個(gè)陷阱。
上述的測(cè)試程序中用到了 Stream.ReadBytes 方法,其實(shí)是一個(gè)擴(kuò)展方法,源程序代碼如下:
using System; |
上述的測(cè)試程序中還使用了 FtpClient 類(lèi),可以參見(jiàn)我的另一篇隨筆“如何直接處理FTP服務(wù)器上的壓縮文件”,其源程序代碼如下:
using System; |
我在上一篇隨筆“【算法】利用有限自動(dòng)機(jī)進(jìn)行字符串匹配”中給出了一道思考題如下:
上面的第二個(gè) C# 程序中有一個(gè) bug,但是這個(gè) bug 在絕大多數(shù)情況下都不會(huì)表現(xiàn)出來(lái)。所以這個(gè)程序能夠 Accepted。
親愛(ài)的讀者,你能夠找出這個(gè) bug 嗎?
提示,這個(gè) bug 和字符串匹配算法無(wú)關(guān),并且第一個(gè) C# 程序中不存在這個(gè) bug 。
上述思考題中的第二個(gè) C# 程序的 Main 方法如下所示:
static void Main() 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); |
這是因?yàn)?Steam.Read 方法在尚未到達(dá)流的末尾情況下可以返回少于所請(qǐng)求的字節(jié),這有可能導(dǎo)致只讀取了部分輸入而產(chǎn)生 bug 。
【編輯推薦】