【转】C# 温故而知新:Stream篇(—)
? 目錄:
什么是Stream?
什么是字節(jié)序列?
Stream的構(gòu)造函數(shù)
Stream的重要屬性及方法
Stream的示例
Stream異步讀寫(xiě)
Stream 和其子類(lèi)的類(lèi)圖
本章總結(jié)
?
?
?
什么是Stream?
MSDN 中的解釋太簡(jiǎn)潔了:?提供字節(jié)序列的一般視圖
(我可不想這么理解,這必定讓我抓狂,我理解的流是向自然界的河流那樣清澈而又美麗,c#中的流也是一樣,許多技術(shù)或者說(shuō)核心技術(shù)都需要流的幫忙)
那什么是字節(jié)序列呢?
其實(shí)簡(jiǎn)單的來(lái)理解的話字節(jié)序列指的是:
字節(jié)對(duì)象都被存儲(chǔ)為連續(xù)的字節(jié)序列,字節(jié)按照一定的順序進(jìn)行排序組成了字節(jié)序列
那什么關(guān)于流的解釋可以抽象為下列情況:
打個(gè)比方:一條河中有一條魚(yú)游過(guò),這個(gè)魚(yú)就是一個(gè)字節(jié),這個(gè)字節(jié)包括魚(yú)的眼睛,嘴巴,等組成8個(gè)二進(jìn)制,顯然這條河就是我們的核心對(duì)象:流
馬上進(jìn)入正題,讓我們來(lái)解釋下c#的 Stream?是如何使用的
讓我們直接溫故或?qū)W習(xí)下Stream類(lèi)的結(jié)構(gòu),屬性和相關(guān)方法
首先是構(gòu)造函數(shù)
Stream 類(lèi)有一個(gè)protected 類(lèi)型的構(gòu)造函數(shù), 但是它是個(gè)抽象類(lèi),無(wú)法直接如下使用
Stream stream = new Stream();所以我們自定義一個(gè)流繼承自Stream 看看哪些屬性必須重寫(xiě)或自定義:
public class MyStreamExample : Stream {public override bool CanRead{get { throw new NotImplementedException(); }}public override bool CanSeek{get { throw new NotImplementedException(); }}public override bool CanWrite{get { throw new NotImplementedException(); }}public override void Flush(){throw new NotImplementedException();}public override long Length{get { throw new NotImplementedException(); }}public override long Position{get{throw new NotImplementedException();}set{throw new NotImplementedException();}}public override int Read(byte[] buffer, int offset, int count){throw new NotImplementedException();}public override long Seek(long offset, SeekOrigin origin){throw new NotImplementedException();}public override void SetLength(long value){throw new NotImplementedException();}public override void Write(byte[] buffer, int offset, int count){throw new NotImplementedException();}}可以看出系統(tǒng)自動(dòng)幫我們實(shí)現(xiàn)了Stream 的抽象屬性和屬性方法
?? 1:? CanRead: 只讀屬性,判斷該流是否能夠讀取:
?? 2:? CanSeek: 只讀屬性,判斷該流是否支持跟蹤查找
?? 3:? CanWrite: 只讀屬性,判斷當(dāng)前流是否可寫(xiě)
*4: void Flush():這點(diǎn)必須說(shuō)得仔細(xì)些:
????當(dāng)我們使用流寫(xiě)文件時(shí),數(shù)據(jù)流會(huì)先進(jìn)入到緩沖區(qū)中,而不會(huì)立刻寫(xiě)入文件,當(dāng)執(zhí)行這個(gè)方法后,緩沖區(qū)的數(shù)據(jù)流會(huì)立即注入基礎(chǔ)流
???? MSDN中的描述:使用此方法將所有信息從基礎(chǔ)緩沖區(qū)移動(dòng)到其目標(biāo)或清除緩沖區(qū),或者同時(shí)執(zhí)行這兩種操作。根據(jù)對(duì)象的狀態(tài),可能需要修
???? 改流內(nèi)的當(dāng)前位置(例如,在基礎(chǔ)流支持查找的情況下即如此)當(dāng)使用?StreamWriter?或?BinaryWriter?類(lèi)時(shí),不要刷新?Stream?基對(duì)象。
???? 而應(yīng)使用該類(lèi)的?Flush?或?Close?方法,此方法確保首先將該數(shù)據(jù)刷新至基礎(chǔ)流,然后再將其寫(xiě)入文件。
(紅色部分為關(guān)鍵請(qǐng)大家務(wù)必能夠理解,今后會(huì)在相應(yīng)的章節(jié)中介紹)
? 5: Length:表示流的長(zhǎng)度
*6: Position屬性:(非常重要)
雖然從字面中可以看出這個(gè)Position屬性只是標(biāo)示了流中的一個(gè)位置而已,可是我們?cè)趯?shí)際開(kāi)發(fā)中會(huì)發(fā)現(xiàn)這個(gè)想法會(huì)非常的幼稚,
很多asp.net項(xiàng)目中文件或圖片上傳中很多朋友會(huì)經(jīng)歷過(guò)這樣一個(gè)痛苦:Stream對(duì)象被緩存了,導(dǎo)致了Position屬性在流中無(wú)法
找到正確的位置,這點(diǎn)會(huì)讓人抓狂,其實(shí)解決這個(gè)問(wèn)題很簡(jiǎn)單,聰明的你肯定想到了,其實(shí)我們每次使用流前必須將Stream.Position
設(shè)置成0就行了,但是這還不能根本上解決問(wèn)題,最好的方法就是用Using語(yǔ)句將流對(duì)象包裹起來(lái),用完后關(guān)閉回收即可。
*7: abstract int Read(byte[] buffer, int offset, int count)
這個(gè)方法包含了3個(gè)關(guān)鍵的參數(shù):緩沖字節(jié)數(shù)組,位移偏量和讀取字節(jié)個(gè)數(shù),每次讀取一個(gè)字節(jié)后會(huì)返回一個(gè)緩沖區(qū)中的總字節(jié)數(shù)
第一個(gè)參數(shù):這個(gè)數(shù)組相當(dāng)于一個(gè)空盒子,Read()方法每次讀取流中的一個(gè)字節(jié)將其放進(jìn)這個(gè)空盒子中。(全部讀完后便可使用buffer字節(jié)數(shù)組了)
第二個(gè)參數(shù):表示位移偏量,指定數(shù)據(jù)從buffer的什么位置開(kāi)始存放
最后一個(gè)參數(shù):就是讀取多少字節(jié)數(shù)。
返回值便是總共讀取了多少字節(jié)數(shù).
*8: abstract long Seek(long offset, SeekOrigin origin)
??? 大家還記得Position屬性么?其實(shí)Seek方法就是重新設(shè)定流中的一個(gè)位置,在說(shuō)明offset參數(shù)作用之前大家先來(lái)了解下SeekOrigin這個(gè)枚舉:
如果 offset 為負(fù),則要求新位置位于 origin 指定的位置之前,其間隔相差 offset 指定的字節(jié)數(shù)。如果 offset 為零 (0),則要求新位置位于由 origin 指定的位置處。
如果 offset 為正,則要求新位置位于 origin 指定的位置之后,其間隔相差 offset 指定的字節(jié)數(shù).
Stream. Seek(-3,Origin.End);? 表示在流末端往前數(shù)第3個(gè)位置
Stream. Seek(0,Origin.Begin); 表示在流的開(kāi)頭位置
Stream. Seek(3,Orig`in.Current); 表示在流的當(dāng)前位置往后數(shù)第三個(gè)位置
查找之后會(huì)返回一個(gè)流中的一個(gè)新位置。其實(shí)說(shuō)道這大家就能理解Seek方法的精妙之處了吧
*9: abstract void Write(byte[] buffer,int offset,int count)
這個(gè)方法包含了3個(gè)關(guān)鍵的參數(shù):緩沖字節(jié)數(shù)組,位移偏量和讀取字節(jié)個(gè)數(shù)
和read方法不同的是 write方法中的第一個(gè)參數(shù)buffer已經(jīng)有了許多byte類(lèi)型
的數(shù)據(jù),我們只需通過(guò)設(shè)置 offset和count來(lái)將buffer中的數(shù)據(jù)寫(xiě)入流中
*10: virtual void Close()
關(guān)閉流并釋放資源,在實(shí)際操作中,如果不用using的話,別忘了使用完流之后將其關(guān)閉
這個(gè)方法特別重要,使用完當(dāng)前流千萬(wàn)別忘記關(guān)閉!
?
為了讓大家能夠快速理解和消化上述屬性和方法我會(huì)寫(xiě)個(gè)示例并且關(guān)鍵部分會(huì)詳細(xì)說(shuō)明
static void Main(string[] args){byte[] buffer = null;string testString = "Stream!Hello world";char[] readCharArray = null;byte[] readBuffer = null;string readString = string.Empty;//關(guān)于MemoryStream 我會(huì)在后續(xù)章節(jié)詳細(xì)闡述using (MemoryStream stream = new MemoryStream()) {Console.WriteLine("初始字符串為:{0}", testString);//如果該流可寫(xiě)if (stream.CanWrite){//首先我們嘗試將testString寫(xiě)入流中//關(guān)于Encoding我會(huì)在另一篇文章中詳細(xì)說(shuō)明,暫且通過(guò)它實(shí)現(xiàn)string->byte[]的轉(zhuǎn)換buffer = Encoding.Default.GetBytes(testString);//我們從該數(shù)組的第一個(gè)位置開(kāi)始寫(xiě),長(zhǎng)度為3,寫(xiě)完之后 stream中便有了數(shù)據(jù)//對(duì)于新手來(lái)說(shuō)很難理解的就是數(shù)據(jù)是什么時(shí)候?qū)懭氲搅髦?#xff0c;在冗長(zhǎng)的項(xiàng)目代碼面前,我碰見(jiàn)過(guò)很//多新手都會(huì)有這種經(jīng)歷,我希望能夠用如此簡(jiǎn)單的代碼讓新手或者老手們?cè)跍毓氏禄A(chǔ)stream.Write(buffer, 0,3);Console.WriteLine("現(xiàn)在Stream.Postion在第{0}位置",stream.Position+1);//從剛才結(jié)束的位置(當(dāng)前位置)往后移3位,到第7位long newPositionInStream =stream.CanSeek? stream.Seek(3, SeekOrigin.Current):0;Console.WriteLine("重新定位后Stream.Postion在第{0}位置", newPositionInStream+1);if (newPositionInStream < buffer.Length){//將從新位置(第7位)一直寫(xiě)到buffer的末尾,注意下stream已經(jīng)寫(xiě)入了3個(gè)數(shù)據(jù)“Str”stream.Write(buffer, (int)newPositionInStream, buffer.Length - (int)newPositionInStream);}//寫(xiě)完后將stream的Position屬性設(shè)置成0,開(kāi)始讀流中的數(shù)據(jù)stream.Position = 0;// 設(shè)置一個(gè)空的盒子來(lái)接收流中的數(shù)據(jù),長(zhǎng)度根據(jù)stream的長(zhǎng)度來(lái)決定readBuffer = new byte[stream.Length];//設(shè)置stream總的讀取數(shù)量 ,//注意!這時(shí)候流已經(jīng)把數(shù)據(jù)讀到了readBuffer中int count = stream.CanRead?stream.Read(readBuffer, 0, readBuffer.Length):0;//由于剛開(kāi)始時(shí)我們使用加密Encoding的方式,所以我們必須解密將readBuffer轉(zhuǎn)化成Char數(shù)組,這樣才能重新拼接成string//首先通過(guò)流讀出的readBuffer的數(shù)據(jù)求出從相應(yīng)Char的數(shù)量int charCount = Encoding.Default.GetCharCount(readBuffer, 0, count);//通過(guò)該Char的數(shù)量 設(shè)定一個(gè)新的readCharArray數(shù)組readCharArray = new char[charCount];//Encoding 類(lèi)的強(qiáng)悍之處就是不僅包含加密的方法,甚至將解密者都能創(chuàng)建出來(lái)(GetDecoder()),//解密者便會(huì)將readCharArray填充(通過(guò)GetChars方法,把readBuffer 逐個(gè)轉(zhuǎn)化將byte轉(zhuǎn)化成char,并且按一致順序填充到readCharArray中)Encoding.Default.GetDecoder().GetChars(readBuffer, 0, count, readCharArray, 0);for (int i = 0; i < readCharArray.Length; i++){readString += readCharArray[i];}Console.WriteLine("讀取的字符串為:{0}", readString);}stream.Close();}Console.ReadLine();}
顯示結(jié)果:
大家需要特別注意的是stream.Positon這個(gè)很神奇的屬性,在復(fù)雜的程序中,往往流對(duì)象操作也會(huì)很復(fù)雜,
一定要切記將stream.Positon設(shè)置在你所需要的正確位置,還有就是 using語(yǔ)句的使用,它會(huì)自動(dòng)銷(xiāo)毀stream對(duì)象,
當(dāng)然Stream.Close()大家都懂的
?
接著讓我們來(lái)說(shuō)下關(guān)于流中怎么實(shí)現(xiàn)異步操作
在Stream基類(lèi)中還有幾個(gè)關(guān)鍵方法,它們能夠很好實(shí)現(xiàn)異步的讀寫(xiě),
異步讀取 public virtual IAsyncResult BeginRead(byte[] buffer,int offset,int count,AsyncCallback callback,Object state) 異步寫(xiě) public virtual IAsyncResult BeginWrite( byte[] buffer, int offset, int count, AsyncCallback callback, Object state ) 結(jié)束異步讀取 public virtual int EndRead( IAsyncResult asyncResult ) 結(jié)束異步寫(xiě) public virtual void EndWrite( IAsyncResult asyncResult )大家很容易的就能發(fā)現(xiàn)前兩個(gè)方法實(shí)現(xiàn)了IAsyncResult接口,后2個(gè)end方法也順應(yīng)帶上了一個(gè)IAsyncResult參數(shù),
其實(shí)并不復(fù)雜,(必須說(shuō)明下?每次調(diào)用?Begin方法時(shí)都必須調(diào)用一次 相對(duì)應(yīng)的end方法)
和一般同步read或write方法一致的是,他們可以當(dāng)做同步方法使用,但是在復(fù)雜的情況下可能也難逃阻塞崩潰等等,但是一旦啟用了
異步之后,這些類(lèi)似于阻塞問(wèn)題會(huì)不復(fù)存在,可見(jiàn)微軟對(duì)于異步的支持正在加大。
?
?最后是有關(guān)c#中Stream類(lèi)和其子類(lèi)的類(lèi)圖
? 類(lèi)圖呢?大家肯定會(huì)這么想把 ^^
? ?為什么這個(gè)在目錄中是灰色的?其實(shí)我個(gè)人覺(jué)得這個(gè)類(lèi)圖不應(yīng)該放在這篇博文中,原因是我們真正理解并熟練操作了Stream的所有子類(lèi)?(大牛除外)
? (這也是我寫(xiě)后續(xù)文章的動(dòng)力之一,寫(xiě)博能很好的提升知識(shí)點(diǎn)的吸收,不僅能幫助別人,也能提高自己的對(duì)于知識(shí)點(diǎn)的理解),所以我想把類(lèi)圖放在這
? ?個(gè)系類(lèi)的總結(jié)篇中
?
本章總結(jié):
本章介紹了流的基本概念和c#中關(guān)于流的基類(lèi)Stream所包含的一些重要的屬性和方法,關(guān)鍵是一些方法和屬性的細(xì)節(jié)和我們操作流對(duì)象時(shí)必須注意的事項(xiàng),
文中很多知識(shí)點(diǎn)都是自身感悟?qū)W習(xí)而來(lái),深夜寫(xiě)文不容易,請(qǐng)大家多多關(guān)注下,下一章將會(huì)介紹操作流類(lèi)的工具:StreamReader 和StreamWriter
敬請(qǐng)期待!
總結(jié)
以上是生活随笔為你收集整理的【转】C# 温故而知新:Stream篇(—)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 办高额信用卡需要哪些条件
- 下一篇: 【转】ABP源码分析三十五:ABP中动态