實作 Stream 類別的資料讀寫操作 – 以FileStream為例

Stream 類別為其他資料流類別的基底類別,針對所要處理的資料來源,我們通常會使用其對應的衍生類別進行資料流的讀寫操作,例如檔案,主要藉由 FileStream 完成相關操作,而我們透過FileInfo來取得指定檔案的 FileStream 物件,例如以下的程式碼:
FileInfo finfo = new FileInfo(path);
FileStream readStream = finfo.OpenRead();
FileStream writeStream = finfo.OpenWrite();
OpenRead方法回傳的是 FileStream 物件,支援檔案的讀取作業,例如上述程式碼的 readStream,另外如果要寫入檔案,則引用OpenWrite取得支援寫入作業的FileStream,例如上述程式碼的 writeStream 變數。
class Program
{
    static void Main(string[] args)
    {
        try
        {
            // read
            Console.Write("\n輸入準備讀取的檔案路徑:");
            string path = Console.ReadLine();
            FileInfo readinfo = new FileInfo(path);
            FileStream readstream = readinfo.OpenRead();
            long l = readstream.Length;
            byte[] bytes = new byte[l];
            int intRead = readstream.Read(bytes, 0, bytes.Length);
            string content = Encoding.UTF8.GetString(bytes);
            Console.WriteLine("檔案長度:{0}", l);
            Console.WriteLine("檔案內容:{0}", content);
            readstream.Close();
            // write
            Console.Write("\n輸入準備寫入的檔案路徑:");
            string writepath = Console.ReadLine();
            FileInfo writeinfo = new FileInfo(writepath);
            FileStream writestream = writeinfo.OpenWrite();
            writestream.Write(bytes, 0, bytes.Length);
            writestream.Close();
            Console.WriteLine("檔案寫入完成");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        Console.ReadKey();
    }
}
類別中的程式碼分成兩段,第一段讀取指定的檔案,第二段將讀取的資料寫入指定的檔案。

第一段首先引用OpenRead方法,取得FileStream資料流物件,接下來引用Length屬性取得資料流長度,據以建立位元組陣列物件bytes,最後引用Read方法,將資料讀入陣列物件,此時陣列物件包含了檔案的內容。

接下來引用OpenWrite方法,取得另外一個檔案的對應資料流物件writestream,將上述取得的位元組陣列,引用Write方法寫入檔案。
輸入準備讀取的檔案路徑:E:\StreamDemo\az.txt
檔案長度:52
檔案內容:?ABCDEFGHIJKLMNOPQRSTUVWXYZ
檔案資料流讀寫

輸入準備寫入的檔案路徑:E:\StreamDemo\za.txt
檔案寫入完成
指定讀取的檔案az.txt,取出其中的內容輸出,接下來指定寫入的檔案za.txt,最後出現「檔案寫入完成」,az.txt的檔案內容複製到za.txt。

FileStream 類別針對檔案進行資料讀寫的動作,若是檔案裏只存在位元組型態的資料,您可以使用這個類別完成所需的作業,上述的範例透過 FileInfo 取得 FileStream ,事實上可以利用建構式產生新的 FileStream 物件,以下是 FileStream 類別所提供的其中一個建構式:
FileStrem(string fileName , FileMode ModeType , FileAccess access)
第一個參數是資料流要參考的檔案路徑字串,並且依據第二個FileMode列舉型別參數所指定的列舉值,設定檔案資料流的開啟模式,可能的模式項目如下表:

FileMode說明
Append6開啟指定的檔案,並且將資料流的位置定於檔案的末端,資料流輸出的資料寫檔案時則附加於檔案的後端。
Create2建立新的檔案,若是指定的檔案已經存在,則直接覆蓋此檔案。
CreateNew1建立新的檔案,與上述Create()不同的地方,在於若是檔案已經存在,則擲回一個 IOException 例外,也就是說,這個列舉值只能建立一個全新的檔案,覆蓋一個已經存在的檔案。
Truncate5開啟一個目前存在的檔案,並且重設其大小為 0 位元組長度,因此以這個列舉參數所開啟的檔案,無法被讀取,而其所進行的動作同上述的Ctrate列舉值對於一個已存在檔案所進行的操作。
Open3開啟已存在的檔案,如果檔案不存在,擲回一個FileNotFoundException的例外。
OpenOrCreate4開啟已存在的檔案,如果檔案不存在,則應該建立新的檔案。

FileMode 依指定的列舉項目開啟、或是建立一個參考全新檔案的資料流,一旦檔案資料物件建立之後,便可以對其參考的檔案進行存取。

最後一個FileAccess 列舉參數指定開啟的檔案資料流讀寫存取,可能的列舉項目如下表:

FileMode說明
Read1檔案的讀取權限, 資料可以從檔案讀取, 與讀/寫存取的 Write 結合。
ReadWrite3讀取和寫入檔案的存取權限, 資料可以寫入檔案和從檔案讀取。
Write2寫入檔案的存取權限, 資料可以寫入檔案, 與讀/寫存取的 Read 結合。

回到上述的範例檔案,嘗試修改程式碼,讀取檔案的部份修改如下:
Console.Write("\n輸入準備讀取的檔案路徑:");
string path = Console.ReadLine();
//FileInfo readinfo = new FileInfo(path);
//FileStream readstream = readinfo.OpenRead();
FileStream readstream = new FileStream(
path, 
FileMode.Open, 
FileAccess.Read);
其中的第二個參數指定 FileMode.Open 表示要開啟檔案,FileAccess.Read表示要讀取檔案。

寫入的部份修改如下:
//FileInfo writeinfo = new FileInfo(writepath);
//FileStream writestream = writeinfo.OpenWrite();
FileStream writestream = new FileStream(
writepath, 
FileMode.Open, 
FileAccess.Write);
第三個參數指定 FileAccess.Write表示要寫入檔案。

完成調整之後,執行結果與駐解的程式碼相同,請自行參考。



沒有留言: