Stream-字元資料讀寫

處理包含Unicode 字元組資料的資料流類別,根據讀取或者寫入的操作,分別以 TextReader 與TextWriter 這兩個類別為基底類別提供所需的支援。

TextReader類別

TextReader類別支援字元資料的讀取,本身是一個抽象類別,其下衍生兩個類別,分別是StreamReader 以及 StringReader ,通常你必須使用這兩個衍生類別建立處理文字資料的資料流物件。

TextReader 類別定義了讀取文字資料所需的通用方法,包含 Read 與 ReadLine ,底下列舉說明之。

Read

Read 方法包含兩個多載的版本,用以讀取輸入資料流中,目前位置的下一個字元,除了無參數的版本,另外一個多載版本接受三個參數,定義如下:
int Read(char[] buffer,int index,int count)
這個版本的方法,從索引值等於 index 的位置開始讀取資料流中的字元,並且讀取 count 參數所指定的字元數目,最後則將讀取的字元放入 buffer 參數的字元陣列物件。

ReadLine

ReadLine 用來讀取一整行的字元資料,定義如下:
public virtual string ReadLine ()
以換行字元或、換行符號或是資料流的未端為標記,讀取一整段的文字回傳。

ReadToEnd

ReadToEnd 直接讀取資料流中,目前位置開始到整個資料流結束的所有字元,並且回傳string 型別結果字串,以下為其定義:
public virtual string ReadToEnd ()
以上列舉的方法均適用於 TextReader類別的子類別,後文討論子類別時,會有進一步的實例說明。

TextWriter

TextWriter 將指定字元資料寫入於資料流,與 TextReader 類別相同,均是文字資料流的基底類別,這個類別衍生的兩個類別,分別是 StreamReader 類別以及 StringWriter ,你同樣必須使用這兩個類別建立文字資料流的實體物件,進行資料流的字元寫入操作。

這個類別所定義的方法為其他兩個字元資料流的通用方法,Write 用以將指定的資料寫入資料流,而 WriteLine 則將指定的整行文字寫入資料流,這兩個方法針對特定資料型別提供對應的多載版本,例如以下的方法將一個 char 型別的字元資料寫入到資料流:
public virtual void Write (char value)
同樣的,將 char 型別的字元資料寫入到資料流的WriteLine 方法,其形式如下:
void WriteLine(char value)
這個方法於寫入的字元資料後面補上一個斷行結束符號,其它型別的原理相同。
Write 以及WriteLine 這兩個方法另外還包含能夠指定寫入位置以及字元長度的版本,定義如下:
void WriteLine(char[] buffer,int index,int count)
其中的參數 buffer 參考寫入的字元資料來源, index 則是指定開始寫入的位置,而 count 則為寫入資料流的字元數目。

另外一個 Write 方法接受同樣的參數值,與 WriteLine 最大的差異在於WriteLine 寫入資料完成之後,隨即補上一個代表行結束的符號。

TextWriter 類別提供將字元寫入資料流的方法, TextReader 類別定義了自資料流中讀取字元資料的方法,組合這兩個類別的功能,可以建立一個具備讀寫字元資料功能的應用程式,而在實作上,一般均是藉由這兩個類別的衍生類別來完成。

TextReader 與 TextWriteer 衍生類別

TextReader 如同 Stream 是抽象基礎類別,定義了字元資料的讀取方法,其下衍生的兩個類別:StreamReader以及StringReader,分別提供讀取不同資料來源的字元資料,列舉說明如下:
StreamReader

從位元組資料流中(Stream)讀取字元,使用這個類別,通常必須將指定的位元組資料流,也就是 Stream 類別的實體物件,當作參數傳入其建構式,建立一個串接 Stream 類別物件的 StreamReader 類別實體,並且引用其提供的方法,進行資料流的讀寫操作。

StringReader

從字串(String)中讀取字元資料,這個衍生的資料流類別,將一段 string 型別的資料當作資料來源,因此在建立這個類別的實體物件之前,必須指定所要讀取的字串物件。
TextWriter 類別同樣衍生了兩個將字元資料寫入資料流或是字元的類別,其中包含了 StreamWriter 以及 StringWriter 。

StreamWriter

這個類別用以建立寫入字元資料的資料流物件,通常我們會將 Stream 類別資料流物件,當作參數傳入其建構式,建立一個串接位元組資料流的 StreamWriter 類別物件,進行資料的讀寫操作。

StringWriter

將字串當作資料的寫入操作來源,寫入資料於字串。

以下針對這幾個衍生類別,說明如何使用這些類別,建立以字元為基礎的資料流,並且進行字元資料的讀取與寫入功能實作。

// StringDemo
class Program
{
    static void Main(string[] args)

    {
        Console.Write("\n輸入準備讀取的檔案路徑:");
        string path = Console.ReadLine();
        FileStream readstream = new FileStream(
            path, FileMode.Open, FileAccess.Read);
        //long l = readstream.Length;
        //byte[] bytes = new byte[l];
        //int intRead = readstream.Read(bytes, 0, bytes.Length);
        //string content = Encoding.UTF8.GetString(bytes);
        StreamReader sr = new StreamReader(readstream);
        string content = sr.ReadToEnd();

        sr.Close();
        readstream.Close();
        Console.WriteLine("檔案內容:{0}", content);

        Console.Write("\n輸入準備寫入的檔案路徑:");
        string writepath = Console.ReadLine();
        FileInfo writeinfo = new FileInfo(writepath);
        FileStream writestream = writeinfo.OpenWrite();
        //writestream.Write(bytes, 0, bytes.Length);
        StreamWriter sw = new StreamWriter(writestream);
        sw.Write(content);
        sw.Close();
        writestream.Close();
        Console.WriteLine("檔案寫入完成");

        Console.ReadKey();
    }
}

討論FileStream時已經看過這個範例,現在將其中讀取檔案內容的程式碼註解,直接將FileStream物件當作參數傳入StreamReader建構式,以建立StreamReader物件參考變數 sr,接下來就可以透過ReadToEnd方法,將其中的內容文字直接取出,不需要再經由位元組轉換。
寫入的部份,則將原來引用Write方法的部份駐解,建立StreamWriter物件sw,引用Write方法,直接將字串寫入。

TextReader 與 TextWriteer可以直接處理字串格式資料,因此不需處理位元組的格式問題。

指定讀取位置

在預設的情形下,每讀取一個位元組,資料流中的指標,便會依序移到下一個位置,除了循序存取資料,你還可以經由設定 Stream 類別的屬性 Position ,隨意的移動指標位置,對資料作隨機式的存取操作,以下為此屬性的定義:
public abstract long Position { get; set; }
這個屬性同時定義了 get 與 set ,因此可以調整目前資料指標的位置,取得的屬性值為目前資料指標的位置。
Console.Write("\n輸入準備讀取的檔案路徑:");
string path = Console.ReadLine();
FileStream readstream = new FileStream(
    path, FileMode.Open, FileAccess.Read);
if (readstream.Length > 10)
    readstream.Position = 10; 
StreamReader sr = new StreamReader(readstream);
string content = sr.ReadToEnd();
在這段程式碼中,首先判斷回傳的FileStrem長度是否超過10,是的話引用Position屬性,將其設定為10,如此一來,接下來讀取的操作將會從資料流的第十個位置開始。

現在假設要開啟的文字檔中,配置從A開始到Z的二十六個英文字母,這段程式讀出的結果,將是 KLMNOPQRSTUVWXYZ 。



沒有留言: