日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

【转】C# Stream篇(—) -- Stream基类

發布時間:2023/12/10 C# 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】C# Stream篇(—) -- Stream基类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?目錄:

什么是Stream?

什么是字節序列?

Stream的構造函數

Stream的重要屬性及方法

Stream的示例

Stream異步讀寫

Stream 和其子類的類圖

本章總結

?

什么是Stream?

MSDN 中的解釋太簡潔了:?提供字節序列的一般視圖

(我可不想這么理解,這定義讓我抓狂,我理解的流是向自然界的河流那樣清澈而又美麗,c#中的流也是一樣,許多技術或者說核心技術都需要流的幫忙)

那什么是字節序列呢?

其實簡單的理解的話,字節序列指的是:

字節對象都被存儲為連續的字節序列,字節按照一定的順序進行排序組成了字節序列

那么關于流的解釋可以抽象為下列情況:

打個比方:一條河中有一條魚游過,這個魚就是一個字節,這個字節包括魚的眼睛,嘴巴,等組成8個二進制,顯然這條河就是我們的核心對象:流

馬上進入正題,讓我們來解釋下c#的 Stream?是如何使用的

讓我們直接溫故或學習下Stream類的結構,屬性和相關方法

首先是構造函數

Stream 類有一個protected 類型的構造函數, 但是它是個抽象類,無法直接如下使用

Stream stream = new Stream();

所以我們自定義一個流繼承自Stream 看看哪些屬性必須重寫或自定義:

?

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();}}

?

可以看出系統自動幫我們實現了Stream 的抽象屬性和屬性方法

?? 1:? CanRead: 只讀屬性,判斷該流是否能夠讀取:

?? 2:? CanSeek: 只讀屬性,判斷該流是否支持跟蹤查找

?? 3:? CanWrite: 只讀屬性,判斷當前流是否可寫

*4: void Flush():這點必須說得仔細些:

????當我們使用流寫文件時,數據流會先進入到緩沖區中,而不會立刻寫入文件,當執行這個方法后,緩沖區的數據流會立即注入基礎流

???? MSDN中的描述:使用此方法將所有信息從基礎緩沖區移動到其目標或清除緩沖區,或者同時執行這兩種操作。根據對象的狀態,可能需要修

???? 改流內的當前位置(例如,在基礎流支持查找的情況下即如此)當使用?StreamWriter?或?BinaryWriter?類時,不要刷新?Stream?基對象。

???? 而應使用該類的?Flush?或?Close?方法,此方法確保首先將該數據刷新至基礎流,然后再將其寫入文件。

(紅色部分為關鍵請大家務必能夠理解,今后會在相應的章節中介紹)

? 5: Length:表示流的長度

*6: Position屬性:(非常重要)

雖然從字面中可以看出這個Position屬性只是標示了流中的一個位置而已,可是我們在實際開發中會發現這個想法會非常的幼稚,

很多asp.net項目中文件或圖片上傳中很多朋友會經歷過這樣一個痛苦:Stream對象被緩存了,導致了Position屬性在流中無法

找到正確的位置,這點會讓人抓狂,其實解決這個問題很簡單,聰明的你肯定想到了,其實我們每次使用流前必須將Stream.Position

設置成0就行了,但是這還不能根本上解決問題,最好的方法就是用Using語句將流對象包裹起來,用完后關閉回收即可。

*7: abstract int Read(byte[] buffer, int offset, int count)

這個方法包含了3個關鍵的參數:緩沖字節數組,位移偏量和讀取字節個數,每次讀取一個字節后會返回一個緩沖區中的總字節數

第一個參數:這個數組相當于一個空盒子,Read()方法每次讀取流中的一個字節將其放進這個空盒子中。(全部讀完后便可使用buffer字節數組了)

第二個參數:表示位移偏量,告訴我們從流中哪個位置(偏移量)開始讀取。

最后一個參數:就是讀取多少字節數。

返回值便是總共讀取了多少字節數.

*8: abstract long Seek(long offset, SeekOrigin origin)

??? 大家還記得Position屬性么?其實Seek方法就是重新設定流中的一個位置,在說明offset參數作用之前大家先來了解下SeekOrigin這個枚舉:

如果 offset 為負,則要求新位置位于 origin 指定的位置之前,其間隔相差 offset 指定的字節數。如果 offset 為零 (0),則要求新位置位于由 origin 指定的位置處。

如果 offset 為正,則要求新位置位于 origin 指定的位置之后,其間隔相差 offset 指定的字節數.

Stream. Seek(-3,Origin.End);? 表示在流末端往前數第3個位置

Stream. Seek(0,Origin.Begin); 表示在流的開頭位置

Stream. Seek(3,Orig`in.Current); 表示在流的當前位置往后數第三個位置

查找之后會返回一個流中的一個新位置。其實說道這大家就能理解Seek方法的精妙之處了吧

*9: abstract void Write(byte[] buffer,int offset,int count)

這個方法包含了3個關鍵的參數:緩沖字節數組,位移偏量和讀取字節個數

和read方法不同的是 write方法中的第一個參數buffer已經有了許多byte類型

的數據,我們只需通過設置 offset和count來將buffer中的數據寫入流中

*10: virtual void Close()

關閉流并釋放資源,在實際操作中,如果不用using的話,別忘了使用完流之后將其關閉

這個方法特別重要,使用完當前流千萬別忘記關閉!

?

為了讓大家能夠快速理解和消化上述屬性和方法我會寫個示例并且關鍵部分會詳細說明

?

static void Main(string[] args){byte[] buffer = null;string testString = "Stream!Hello world";char[] readCharArray = null;byte[] readBuffer = null;string readString = string.Empty;//關于MemoryStream 我會在后續章節詳細闡述using (MemoryStream stream = new MemoryStream()) {Console.WriteLine("初始字符串為:{0}", testString);//如果該流可寫if (stream.CanWrite){//首先我們嘗試將testString寫入流中//關于Encoding我會在另一篇文章中詳細說明,暫且通過它實現string->byte[]的轉換buffer = Encoding.Default.GetBytes(testString);//我們從該數組的第一個位置開始寫,長度為3,寫完之后 stream中便有了數據//對于新手來說很難理解的就是數據是什么時候寫入到流中,在冗長的項目代碼面前,我碰見過很//多新手都會有這種經歷,我希望能夠用如此簡單的代碼讓新手或者老手們在溫故下基礎stream.Write(buffer, 0,3);Console.WriteLine("現在Stream.Postion在第{0}位置",stream.Position+1);//從剛才結束的位置(當前位置)往后移3位,到第7位long newPositionInStream =stream.CanSeek? stream.Seek(3, SeekOrigin.Current):0;Console.WriteLine("重新定位后Stream.Postion在第{0}位置", newPositionInStream+1);if (newPositionInStream < buffer.Length){//將從新位置(第7位)一直寫到buffer的末尾,注意下stream已經寫入了3個數據“Str”stream.Write(buffer, (int)newPositionInStream, buffer.Length - (int)newPositionInStream);}//寫完后將stream的Position屬性設置成0,開始讀流中的數據stream.Position = 0;// 設置一個空的盒子來接收流中的數據,長度根據stream的長度來決定readBuffer = new byte[stream.Length];//設置stream總的讀取數量 ,//注意!這時候流已經把數據讀到了readBuffer中int count = stream.CanRead?stream.Read(readBuffer, 0, readBuffer.Length):0;//由于剛開始時我們使用加密Encoding的方式,所以我們必須解密將readBuffer轉化成Char數組,這樣才能重新拼接成string//首先通過流讀出的readBuffer的數據求出從相應Char的數量int charCount = Encoding.Default.GetCharCount(readBuffer, 0, count);//通過該Char的數量 設定一個新的readCharArray數組readCharArray = new char[charCount];//Encoding 類的強悍之處就是不僅包含加密的方法,甚至將解密者都能創建出來(GetDecoder()),//解密者便會將readCharArray填充(通過GetChars方法,把readBuffer 逐個轉化將byte轉化成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();}

?


顯示結果:

大家需要特別注意的是stream.Positon這個很神奇的屬性,在復雜的程序中,往往流對象操作也會很復雜,

一定要切記將stream.Positon設置在你所需要的正確位置,還有就是 using語句的使用,它會自動銷毀stream對象,

當然Stream.Close()大家都懂的

?

接著讓我們來說下關于流中怎么實現異步操作

在Stream基類中還有幾個關鍵方法,它們能夠很好實現異步的讀寫,

?

異步讀取 public virtual IAsyncResult BeginRead(byte[] buffer,int offset,int count,AsyncCallback callback,Object state) 異步寫 public virtual IAsyncResult BeginWrite( byte[] buffer, int offset, int count, AsyncCallback callback, Object state ) 結束異步讀取 public virtual int EndRead( IAsyncResult asyncResult ) 結束異步寫 public virtual void EndWrite( IAsyncResult asyncResult )

?

大家很容易的就能發現前兩個方法實現了IAsyncResult接口,后2個end方法也順應帶上了一個IAsyncResult參數,

其實并不復雜,(必須說明下?每次調用?Begin方法時都必須調用一次 相對應的end方法)

和一般同步read或write方法一致的是,他們可以當做同步方法使用,但是在復雜的情況下可能也難逃阻塞崩潰等等,但是一旦啟用了

異步之后,這些類似于阻塞問題會不復存在,可見微軟對于異步的支持正在加大。

?

?最后是有關c#中Stream類和其子類的類圖

? 類圖呢?大家肯定會這么想把 ^^

? ?為什么這個在目錄中是灰色的?其實我個人覺得這個類圖不應該放在這篇博文中,原因是我們真正理解并熟練操作了Stream的所有子類?(大牛除外)

? (這也是我寫后續文章的動力之一,寫博能很好的提升知識點的吸收,不僅能幫助別人,也能提高自己的對于知識點的理解),所以我想把類圖放在這

? ?個系類的總結篇中

?

本章總結:

本章介紹了流的基本概念和c#中關于流的基類Stream所包含的一些重要的屬性和方法,關鍵是一些方法和屬性的細節和我們操作流對象時必須注意的事項,

文中很多知識點都是自身感悟學習而來,深夜寫文不容易,請大家多多關注下,下一章將會介紹操作流類的工具:StreamReader 和StreamWriter

敬請期待!

總結

以上是生活随笔為你收集整理的【转】C# Stream篇(—) -- Stream基类的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。