標籤:style io ar os 使用 sp strong 檔案 資料
IO操作基本上需要用到Stream相關的子類,因此這類問題在CSDN問得也是比較多。其實對於Stream來說,操作起來比較簡單,只要對細節的處理稍微注意一下,相信在使用它的時候也會得心應手。
對於Stream相關的問題,大致分如下幾類。
問題一,基本操作的問題;
問題二,編碼的問題;
問題三,尾部處理問題;
問題四,Stream緩衝問題;
問題五,資源釋放問題;
最後一個問題,說說如何使用Stream來更新大檔案部分資料。
對於問題一,基本操作的問題,主要是讀寫問題,主要是出現在檔案資料比較大,需要迴圈寫或者讀的時候。此時正確讀的形式如下。
// Open a file to read
using( FileStream fs = new FileStream( yourFile,
FileMode.Open, FileAccess.Read,
FileShare.None ) )
{
int nRealRead = 0;
byte[] bBuffer = new byte[1024];
do
{
// Read data
nRealRead = fs.Read( bBuffer, 0, 1024 );
// Output data
Debug.WriteLine( Encoding.Default.GetString( bBuffer, 0, nRealRead ) );
}while( nRealRead == 1024 );
}
可是大多數人第一次完成這樣操作的時候,都會在“nRealRead = fs.Read( bBuffer, 0, 1024 );”這一句犯錯誤。認為第二個參數的位移量對於Stream而設的,所以認為應該用累加的值,也就是目前總共讀了多少的位元組數。這裡需要理解一下Stream的操作,當進行讀或者寫操作的時候,Stream的遊標會根據所讀或者所寫得位元組而自動向前跟進;其次Stream.Read或者Stream.Write這兩個方法中第二個參數是針對第一個Buffer參數而言的,而不是對於Stream的,因此不要在這個地方犯錯誤。
基本問題還牽扯的就是檔案開啟的方式。有人經常問,如何同時用兩個Stream開啟同一個檔案。其實預設的Stream開啟檔案是獨享的,因此當不指明檔案為訪問共用的時候,後開啟檔案操作就會出現異常,因此需要向我上面所寫的那樣。還有,如果需要指定當前Stream的起始位置,可以通過Seek方法或者設定Position屬性來完成。
對於問題二,編碼問題。有人使用Stream的子類,例如StreamReader之類來開啟一個文字檔,發現讀出來的資料是亂碼,造成這個原因大多數由於檔案中含有中文字元,同時開啟檔案的時候沒有指明編碼方式。由於英文和中文的編碼方式不同,因此在不指明編碼的時候有時會造成讀取中文錯誤。此時只要使用StreamReader類型中含有Encoding參數的建構函式即可,例如:
using( StreamReader sr = new StreamReader( yourFile, Encoding.Default ) )
這裡只是採用系統預設的編碼方式,但有可能不太適合你檔案的編碼方式,因此需要在實際應用去調試和變換這個參數。
問題三是,Stream尾部處理問題。此類問題所展現的現象如,複製檔案的時候檔案會增大。因此在使用Stream.Read和Stream.Write的時候,要通過方法的傳回值,來標明真正讀和寫的位元組數,就像前面所寫的那樣。
// Read data
nRealRead = fs.Read( bBuffer, 0, 1024 );
// Output data
Debug.WriteLine( Encoding.Default.GetString( bBuffer, 0, nRealRead ) );
此時在輸出的時候用的不是“1024”,而是“nRealRead”做為位元組有效標示。
對於問題四,Stream緩衝的問題,這主要表現在寫的時候。為了避免頻繁操作IO而降低效率,大多數Stream採用非同步寫的方式,也就是Stream對象要配備有一定的緩衝,來暫時儲存寫的資料。但緩衝是有限的,當緩衝已滿後會造成後續寫的資料不能寫入,從而導致資料丟失。那麼此時需要顯示的調用Stream.Flush方法,來把緩衝的資料寫入到檔案中並清空緩衝。其實這並不是唯一方法,在一些Stream的子類中還提供了設定BufferSize的方法,或者提供了設定AutoFlush屬性來實現自動寫入等等,因此這裡大家可以根據不同需要而選擇不同方法來完成。
對於Stream的釋放問題,這可能不單單是使用Stream的問題,可能是使用C#編程而造成的不良習慣。雖說C#的資源是受託管的,但是對於Stream來說,如果不及時釋放,那麼當其他線程或者進程使用此檔案的時候就會造成無法開啟的現象(由於Stream大多數都是以獨享方式開啟),而且沒有及時關閉,所佔用的Buffer無法及時釋放。
因此養成一個好的習慣至關重要。其實釋放Stream很簡單,要麼顯示的調用其的Close和Dispose這兩個方法,要麼使用using程式塊,就像我前面所寫的那樣。
最後一個就是如何使用Stream來更新大檔案。比較常見的就是,當檔案比較大,但是需要修改的部分很少,因此想要通過Stream直接在某個位置進行類似於刪除、插入或者替換等操作。
對於一個檔案的更新操作,大致分為三種,這裡主要是考慮更新的位置和更新資料長度。
第一種對於檔案尾擴充的操作,內容長度不限;
第二種等位元組的替換操作,位置不限;
最後一種就是位置不固定,位元組數不確定。
上面所說的前兩種,進行處理比較簡單。對於第一種,只要設定FileMode的時候增加Append標示即可。而對於等位元組的替換,就更簡單了,直接通過Stream.Seek找到指定的位置,然後調用Stream.Write即可。
而最後一個,是最麻煩的。比較簡單的解決方式,建立一個臨時檔案,然後一邊讀一邊寫,遇到需要修改的,先讀出來再修改最後再寫入。等全部寫完了,刪除舊檔案,修改臨時檔案的名稱為原來名字。
比較麻煩的解決方式,就是通過Share方式,用一個讀Stream和一個寫Stream直接操縱源檔案。這裡需要注意的是,為了保證新寫的資料不要衝掉還沒讀出來的資料,也就是說要控制寫Stream所寫的位置不要超過要讀的位置。舉例說,目前需要讀的位置是檔案的800位元組處,也就是說800位元組以後還沒讀出來處理,此時寫Stream在寫完資料後,Stream的位置不能超過800位元組,如果寫採用的是緩衝,那麼超過800位置的資料不要立刻通過Flush進行提交。總的來說,通過兩個Stream來操作同一個檔案,對於這一點要特別注意,處理不好要造成死迴圈。
c#中Stream