如何有效使用C#讀取檔案 你平時是怎麼讀取檔案的?使用流讀取。是的沒錯,C#給我們提供了非常強大的類庫(又一次吹捧了.NET一番),裡面封裝了幾乎所有我們可以想到的和我們沒有想到的類,流是讀取檔案的一般手段,那麼你真的會用它讀取檔案中的資料了嗎?真的能讀完全嗎? 通常我們讀取一個檔案使用如下的步驟: 1、聲明並使用File的OpenRead執行個體化一個檔案流對象,就像下面這樣 FileStream fs = File.OpenRead(filename); 或者 FileStream fs = FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); 2、準備一個存放檔案內容的位元組數組,fs.Length將得到檔案的實際大小,就像下面這樣 byte[] data = new byte[fs.Length]; 3、哇!開始讀了,調用一個檔案流的一個方法讀取資料到data數組中 fs.Read (data, 0, data.Length); 呵呵!我們唯寫了3句就可以把檔案裡面的內容原封不動的讀出來,真是太簡潔了!可以這段代碼真的能像你預期的那樣工作嗎?答案是:幾乎可以!在大部分情況下上面的代碼工作的很好,但是我們應該注意Read方法是有傳回值的,既然有傳回值那麼一定有其道理,如果按照上面的寫法完全可以是一個沒有傳回值的函數。我想傳回值的目的是,為了給我們一個機會判斷實際讀取檔案的大小,從而來判斷檔案是否已經完全讀完。所以上面的代碼不能保證我們一定讀完了檔案裡面的所有位元組(雖然在很多情況下是讀完了)。下面的方法提供了一個比上面方法更安全的方法,來保證檔案被完全讀出 public static void SafeRead (Stream stream, byte[] data){ int offset=0; int remaining = data.Length; // 只要有剩餘的位元組就不停的讀 while (remaining > 0){ int read = stream.Read(data, offset, remaining); if (read <= 0) throw new EndOfStreamException("檔案讀取到"+read.ToString()+"失敗!"); // 減少剩餘的位元組數 remaining -= read; // 增加位移量 offset += read; } } 有些情況下你不知道流實際的長度比如:網路流。此時可以使用類似的方法讀取流直到流裡面的資料完全讀取出來為止。我們可以先初始化一段緩衝,再將流讀出來的流資訊寫到記憶體流裡面,就像下面這樣: public static byte[] ReadFully (Stream stream){ // 初始化一個32k的緩衝 byte[] buffer = new byte[32768]; using (MemoryStream ms = new MemoryStream()){ //返回結果後會自動回收調用該對象的Dispose方法釋放記憶體 // 不停的讀取 while (true){ int read = stream.Read (buffer, 0, buffer.Length); // 直到讀取完最後的3M資料就可以返回結果了 if (read <= 0) return ms.ToArray(); ms.Write (buffer, 0, read); } } } 雖然上面的例子都比較簡單,效果也不是很明顯(大部分都是對的),也許你早就會了,沒關係這篇文章本來就是寫給初學者的。 下面的方法提供了一種使用指定緩衝長度的方式讀取流,雖然在很多情況下你可以直接使用Stream.Length得到流的長度,但是不是所有的流都可以得到。 public static byte[] Read2Buffer (Stream stream, int BufferLen){ // 如果指定的無效長度的緩衝區,則指定一個預設的長度作為緩衝大小 if (BufferLen < 1){ BufferLen = 0x8000; } // 初始化一個緩衝區 byte[] buffer = new byte[BufferLen]; int read=0; int block; // 每次從流中讀取緩衝大小的資料,知道讀取完所有的流為止 while ( (block = stream.Read(buffer, read, buffer.Length-read)) > 0){ // 重新設定讀取位置 read += block; // 檢查是否到達了緩衝的邊界,檢查是否還有可以讀取的資訊 if (read == buffer.Length){ // 嘗試讀取一個位元組 int nextByte = stream.ReadByte(); // 讀取失敗則說明讀取完成可以返回結果 if (nextByte==-1){ return buffer; } // 調整數組大小準備繼續讀取 byte[] newBuf = new byte[buffer.Length*2]; Array.Copy(buffer, newBuf, buffer.Length); newBuf[read]=(byte)nextByte; buffer = newBuf;// buffer是一個引用(指標),這裡意在重新設定buffer指標指向一個更大的記憶體 read++; } } // 如果緩衝太大則使用ret來收縮前面while讀取的buffer,然後直接返回 byte[] ret = new byte[read]; Array.Copy(buffer, ret, read); return ret; } |