C# Stream作用IO操作基本上需要用到Stream相關(guān)的子類,因此這類問題在CSDN問得也是比較多。其實(shí)對(duì)于Stream來說,操作起來比較簡(jiǎn)單,只要對(duì)細(xì)節(jié)的處理稍微注意一下,相信在使用它的時(shí)候也會(huì)得心應(yīng)手。對(duì)于Stream相關(guān)的問題,大致分如下幾類。問題一,基本操作的問題;問題二,編碼的問題;問題三,尾部處理問題;問題四,Stream緩存問題;問題五,資源釋放問題;最后一個(gè)問題,說說如何使用Stream來更新大文件部分?jǐn)?shù)據(jù)。
對(duì)于問題一,基本操作的問題,主要是讀寫問題,主要是出現(xiàn)在文件數(shù)據(jù)比較大,需要循環(huán)寫或者讀的時(shí)候。此時(shí)正確讀的形式如下。
using ( FileStream fs = new FileStream ( yourFile, FileMode.Open,FileAccess.Read,FileShare.None ) )
{ int nRealRead = 0; byte[] bBuffer = new byte[1024];
do
nRealRead = fs.Read( bBuffer, 0, 1024 );
Debug.WriteLine( Encoding.Default.GetString( bBuffer, 0, nRealRead ) );
}while( nRealRead == 1024 );
}
可是大多數(shù)人第一次完成這樣操作的時(shí)候,都會(huì)在“nRealRead = fs.Read( bBuffer, 0, 1024 );”這一句犯錯(cuò)誤。認(rèn)為第二個(gè)參數(shù)的偏移量對(duì)于Stream而設(shè)的,所以認(rèn)為應(yīng)該用累加的值,也就是目前總共讀了多少的字節(jié)數(shù)。這里需要理解一下Stream的操作,當(dāng)進(jìn)行讀或者寫操作的時(shí)候,Stream的游標(biāo)會(huì)根據(jù)所讀或者所寫得字節(jié)而自動(dòng)向前跟進(jìn);其次Stream.Read或者Stream.Write這兩個(gè)方法中第二個(gè)參數(shù)是針對(duì)第一個(gè)Buffer參數(shù)而言的,而不是對(duì)于Stream的,因此不要在這個(gè)地方犯錯(cuò)誤。
基本問題還牽扯的就是文件打開的方式。有人經(jīng)常問,如何同時(shí)用兩個(gè)Stream打開同一個(gè)文件。其實(shí)默認(rèn)的Stream打開方式是獨(dú)享的,因此當(dāng)不指明文件為訪問共享的時(shí)候,后打開文件操作就會(huì)出現(xiàn)異常,因此需要向我上面所寫的那樣。還有,如果需要指定當(dāng)前Stream的起始位置,可以通過Seek方法或者設(shè)置Position屬性來完成。
對(duì)于問題二,編碼問題。有人使用Stream的子類,例如StreamReader之類來打開一個(gè)文本文件,發(fā)現(xiàn)讀出來的數(shù)據(jù)是亂碼,造成這個(gè)原因大多數(shù)由于文件中含有中文字符,同時(shí)打開文件的時(shí)候沒有指明編碼方式。由于英文和中文的編碼方式不同,因此在不指明編碼的時(shí)候有時(shí)會(huì)造成讀取中文錯(cuò)誤。此時(shí)只要使用StreamReader類型中含有Encoding參數(shù)的構(gòu)造函數(shù)即可,例如:using( StreamReader sr = new StreamReader( yourFile, Encoding.Default ) )
這里只是采用系統(tǒng)默認(rèn)的編碼方式,但有可能不太適合你文件的編碼方式,因此需要在實(shí)際應(yīng)用去調(diào)試和變換這個(gè)參數(shù)。
問題三是,Stream尾部處理問題。此類問題所展現(xiàn)的現(xiàn)象如,復(fù)制文件的時(shí)候文件會(huì)增大。因此在使用Stream.Read和Stream.Write的時(shí)候,要通過方法的返回值,來標(biāo)明真正讀和寫的字節(jié)數(shù),就像前面所寫的那樣。
nRealRead = fs.Read( bBuffer, 0, 1024 ); // Read data
Debug.WriteLine( Encoding.Default.GetString( bBuffer, 0, nRealRead ) );
此時(shí)在輸出的時(shí)候用的不是“1024”,而是“nRealRead”做為字節(jié)有效標(biāo)示。
對(duì)于問題四,Stream緩存的問題,這主要表現(xiàn)在寫的時(shí)候。為了避免頻繁操作IO而降低效率,大多數(shù)Stream采用異步寫的方式,也就是Stream對(duì)象要配備有一定的緩存,來暫時(shí)保存寫的數(shù)據(jù)。但緩存是有限的,當(dāng)緩存已滿后會(huì)造成后續(xù)寫的數(shù)據(jù)不能寫入,從而導(dǎo)致數(shù)據(jù)丟失。那么此時(shí)需要顯示的調(diào)用Stream.Flush方法,來把緩存的數(shù)據(jù)寫入到文件中并清空緩存。其實(shí)這并不是唯一方法,在一些Stream的子類中還提供了設(shè)置BufferSize的方法,或者提供了設(shè)置AutoFlush屬性來實(shí)現(xiàn)自動(dòng)寫入等等,因此這里大家可以根據(jù)不同需要而選擇不同方法來完成。
對(duì)于Stream的釋放問題,這可能不單單是使用Stream的問題,可能是使用C#編程而造成的不良習(xí)慣。雖說C#的資源是受托管的,但是對(duì)于Stream來說,如果不及時(shí)釋放,那么當(dāng)其他線程或者進(jìn)程使用此文件的時(shí)候就會(huì)造成無法打開的現(xiàn)象(由于Stream大多數(shù)都是以獨(dú)享方式打開),而且沒有及時(shí)關(guān)閉,所占用的Buffer無法及時(shí)釋放。因此養(yǎng)成一個(gè)好的習(xí)慣至關(guān)重要。其實(shí)釋放Stream很簡(jiǎn)單,要么顯示的調(diào)用其的Close和Dispose這兩個(gè)方法,要么使用using程序塊,就像我前面所寫的那樣。
最后一個(gè)就是如何使用Stream來更新大文件。比較常見的就是,當(dāng)文件比較大,但是需要修改的部分很少,因此想要通過Stream直接在某個(gè)位置進(jìn)行類似于刪除、插入或者替換等操作。對(duì)于一個(gè)文件的更新操作,大致分為三種,這里主要是考慮更新的位置和更新數(shù)據(jù)長(zhǎng)度。
第一種對(duì)于文件尾擴(kuò)展的操作,內(nèi)容長(zhǎng)度不限;
第二種等字節(jié)的替換操作,位置不限;
最后一種就是位置不固定,字節(jié)數(shù)不確定。
上面所說的前兩種,進(jìn)行處理比較簡(jiǎn)單。對(duì)于第一種,只要設(shè)置FileMode的時(shí)候增加Append標(biāo)示即可。而對(duì)于等字節(jié)的替換,就更簡(jiǎn)單了,直接通過Stream.Seek找到指定的位置,然后調(diào)用Stream.Write即可。
而最后一個(gè),是最麻煩的。比較簡(jiǎn)單的解決方式,創(chuàng)建一個(gè)臨時(shí)文件,然后一邊讀一邊寫,遇到需要修改的,先讀出來再修改最后再寫入。等全部寫完了,刪除舊文件,修改臨時(shí)文件的名稱為原來名字。比較麻煩的解決方式,就是通過Share方式,用一個(gè)讀Stream和一個(gè)寫Stream直接操縱源文件。這里需要注意的是,為了保證新寫的數(shù)據(jù)不要沖掉還沒讀出來的數(shù)據(jù),也就是說要控制寫Stream所寫的位置不要超過要讀的位置。舉例說,目前需要讀的位置是文件的800字節(jié)處,也就是說800字節(jié)以后還沒讀出來處理,此時(shí)寫Stream在寫完數(shù)據(jù)后,Stream的位置不能超過800字節(jié),如果寫采用的是緩存,那么超過800位置的數(shù)據(jù)不要立刻通過Flush進(jìn)行提交??偟膩碚f,通過兩個(gè)Stream來操作同一個(gè)文件,對(duì)于這一點(diǎn)要特別注意,處理不好要造成死循環(huán)。
Stream的問題相對(duì)比較簡(jiǎn)單,大多數(shù)人操作的時(shí)候不注意細(xì)節(jié)。所以我這里只是稍加說明,不做特別細(xì)的說明。