C# FileStream檔案讀寫詳解_C#教程

來源:互聯網
上載者:User

FileStream對象表示在磁碟或網路路徑上指向檔案的流。這個類提供了在檔案中讀寫位元組的方法,但經常使用StreamReader或StreamWriter執行這些功能。這是因為FileStream類操作的是位元組和位元組數組,而Stream類操作的是字元資料。字元資料便於使用,但是有些操作,比如隨機檔案訪問(訪問檔案中間某點的資料),就必須由FileStream對象執行,稍後對此進行介紹。

還有幾種方法可以建立FileStream對象。建構函式具有許多不同的重載版本,最簡單的建構函式僅僅帶有兩個參數,即檔案名稱和FileMode枚舉值。

複製代碼 代碼如下:

FileStream aFile = new FileStream(filename, FileMode.Member);

FileMode枚舉有幾個成員,規定了如何開啟或建立檔案。稍後介紹這些枚舉成員。另一個常用的建構函式如下:

複製代碼 代碼如下:

FileStream aFile = new FileStream(filename, FileMode.Member, FileAccess. Member);

第三個參數是FileAccess枚舉的一個成員,它指定了流的作用。FileAccess枚舉的成員如表22-6所示。

表  22-6

成員 說明
Read 開啟檔案,用於唯讀
Write 開啟檔案,用於唯寫
ReadWrite 開啟檔案,用於讀寫

對檔案進行不是FileAccess枚舉成員指定的操作會導致拋出異常。此屬性的作用是,基於使用者的身分識別驗證層級改變使用者對檔案的存取權限。

在FileStream建構函式不使用FileAccess枚舉參數的版本中,使用預設值FileAccess. ReadWrite。

FileMode枚舉成員如表22-7所示。使用每個值會發生什麼,取決於指定的檔案名稱是否表示已有的檔案。注意這個表中的項表示建立流時該流指向檔案中的位置,下一節將詳細討論這個主題。除非特別說明,否則流就指向檔案的開頭。

表  22-7

成員 檔案存在 檔案不存在
Append 開啟檔案,流指向檔案的末尾,只能與枚舉FileAccess.Write聯合使用 建立一個新檔案。只能與枚舉FileAccess.Write聯合使用
Create 刪除該檔案,然後建立新檔案 建立新檔案
CreateNew 拋出異常 建立新檔案
Open 開啟現有的檔案,流指向檔案的開頭 拋出異常
OpenOrCreate 開啟檔案,流指向檔案的開頭 建立新檔案
Truncate 開啟現有檔案,清除其內容。流指向檔案的開頭,保留檔案的初始建立日期 拋出異常

File和FileInfo類都提供了OpenRead()和OpenWrite()方法,更易於建立FileStream對象。前者開啟了唯讀訪問的檔案,後者只允許寫入檔案。這些都提供了捷徑,因此不必以FileStream建構函式的參數形式提供前面所有的資訊。例如,下面的程式碼開啟了用於唯讀訪問的Data.txt檔案:

複製代碼 代碼如下:

FileStream aFile = File.OpenRead("Data.txt");

注意下面的代碼執行同樣的功能:

複製代碼 代碼如下:

FileInfo aFileInfo = new FileInfo("Data.txt");
FileStream aFile = aFile.OpenRead();

1. 檔案位置

FileStream類維護內部檔案指標,該指標指向檔案中進行下一次讀寫操作的位置。在大多數情況下,當開啟檔案時,它就指向檔案的開始位置,但是此指標可以修改。這允許應用程式在檔案的任何位置讀寫,隨機訪問檔案,或直接跳到檔案的特定位置上。當處理大型檔案時,這非常省時,因為馬上可以定位到正確的位置。

實現此功能的方法是Seek()方法,它有兩個參數:第一個參數規定檔案指標以位元組為單位的移動距離。第二個參數規定開始計算的起始位置,用SeekOrigin枚舉的一個值表示。Seek Origin枚舉包含3個值:Begin、Current和End。

例如,下面的程式碼將檔案指標移動到檔案的第8個位元組,其起始位置就是檔案的第1個位元組:

複製代碼 代碼如下:

aFile.Seek(8,SeekOrigin.Begin);

下面的程式碼將指標從當前位置開始向前移動2個位元組。如果在上面的程式碼之後執行下面的代碼,檔案指標就指向檔案的第10個位元組:

複製代碼 代碼如下:

aFile.Seek(2,SeekOrigin.Current);

注意讀寫檔案時,檔案指標也會改變。在讀取了10個位元組之後,檔案指標就指向被讀取的第10個位元組之後的位元組。

也可以規定負尋找位置,這可以與SeekOrigin.End枚舉值一起使用,尋找靠近檔案末端的位置。下面的代碼會尋找檔案中倒數第5個位元組:

複製代碼 代碼如下:

aFile.Seek(–5, SeekOrigin.End);

以這種方式訪問的檔案有時稱為隨機訪問檔案,因為應用程式可以訪問檔案中的任何位置。稍後介紹的Stream類可以連續地訪問檔案,不允許以這種方式操作檔案指標。

2. 讀取資料

使用FileStream類讀取資料不像使用本章後面介紹的StreamReader類讀取資料那樣容易。這是因為FileStream類只能處理原始位元組(raw byte)。處理原始位元組的功能使FileStream類可以用於任何資料檔案,而不僅僅是文字檔。通過讀取位元組資料,FileStream對象可以用於讀取映像和聲音的檔案。這種靈活性的代價是,不能使用FileStream類將資料直接讀入字串,而使用StreamReader類卻可以這樣處理。但是有幾種轉換類可以很容易地將位元組數群組轉換為字元數組,或者進行相反的操作。

FileStream.Read()方法是從FileStream對象所指向的檔案中訪問資料的主要手段。這個方法從檔案中讀取資料,再把資料寫入一個位元組數組。它有三個參數:第一個參數是傳輸進來的位元組數組,用以接受FileStream對象中的資料。第二個參數是位元組數組中開始寫入資料的位置。它通常是0,表示從數組開端向檔案中寫入資料。最後一個參數指定從檔案中讀出多少位元組。

下面的樣本示範了從隨機訪問檔案中讀取資料。要讀取的檔案實際是為此樣本建立的類檔案。

試試看:從隨機訪問檔案中讀取資料

(1) 在目錄C:\BegVCSharp\Chapter22下建立一個新的控制台應用程式ReadFile。

(2) 在Program.cs檔案的頂部添加下面的using指令:

複製代碼 代碼如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

(3) 在Main()方法中添加下面的代碼:

複製代碼 代碼如下:

static void Main(string[] args)
{
   byte[] byData = new byte[200];
   char[] charData = new Char[200];
 
   try
   {
      FileStream aFile = new FileStream("E:\\Workplace\\TestSolution\\Test.ConsoleApplication\\Program.cs",FileMode.Open);
      aFile.Seek(135,SeekOrigin.Begin);
      aFile.Read(byData,0,200);
   }
   catch(IOException e)
   {
      Console.WriteLine("An IO exception has been thrown!");
      Console.WriteLine(e.ToString());
      Console.ReadKey();
      return;
  }
 
   Decoder d = Encoding.UTF8.GetDecoder();
   d.GetChars(byData, 0, byData.Length, charData, 0);
 
   Console.WriteLine(charData);
   Console.ReadKey();
}

(4) 運行應用程式。結果如圖22-2所示。

圖 22-2

樣本的說明
此應用程式開啟自己的.cs檔案,用於讀取。它在下面的程式碼中使用..字串向上逐級導航兩個目錄,找到該檔案:

複製代碼 代碼如下:

FileStream aFile = new FileStream("E:\\Workplace\\TestSolution\\Test.ConsoleApplication\\Program.cs",FileMode.Open);

下面兩行代碼實現尋找工作,並從檔案的具體位置讀取位元組:

複製代碼 代碼如下:

aFile.Seek(135,SeekOrigin.Begin);
aFile.Read(byData,0,200);

第一行代碼將檔案指標移動到檔案的第135個位元組。在Program.cs中,這是namespace的 “n”;其前面的135個字元是using指令和相關的#region。第二行將接下來的200個位元組讀入到byData位元組數組中。

注意這兩行代碼封裝在try…catch塊中,以處理可能拋出的異常。

複製代碼 代碼如下:

try
{
    aFile.Seek(135,SeekOrigin.Begin);
    aFile.Read(byData,0,100);
}
catch(IOException e)
{
    Console.WriteLine("An IO exception has been thrown!");
    Console.WriteLine(e.ToString());
    Console.ReadKey();
    return;
}

檔案IO涉及到的所有操作都可以拋出類型為IOException的異常。所有產品代碼都必須包含錯誤處理,尤其是處理檔案系統時更是如此。本章的所有樣本都具有錯誤處理的基本形式。

從檔案中擷取了位元組數組後,就需要將其轉換為字元數組,以便在控制台顯示它。為此,使用System.Text命名空間的Decoder類。此類用於將原始位元組轉換為更有用的項,比如字元:

複製代碼 代碼如下:

Decoder d = Encoding.UTF8.GetDecoder();
d.GetChars(byData, 0, byData.Length, charData, 0);

這些代碼基於UTF8編碼模式建立了Decoder對象。這就是Unicode編碼模式。然後調用GetChars()方法,此方法提取位元組數組,將它轉換為字元數組。完成之後,就可以將字元數組輸出到控制台。

3. 寫入資料

向隨機訪問檔案中寫入資料的過程與從中讀取資料非常類似。首先需要建立一個位元組數組;最簡單的辦法是首先構建要寫入檔案的字元數組。然後使用Encoder對象將其轉換為位元組數組,其用法非常類似於Decoder。最後調用Write()方法,將位元組數組傳送到檔案中。

下面構建一個簡單的樣本示範其過程。

試試看:將資料寫入隨機訪問檔案

(1) 在C:\BegVCSharp\Chapter22目錄下建立一個新的控制台應用程式WriteFile。

(2) 如上所示,在Program.cs檔案頂部添加下面的using指令:

複製代碼 代碼如下:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

(3) 在Main()方法中添加下面的代碼:

複製代碼 代碼如下:

static void Main(string[] args)
{
   byte[] byData;
   char[] charData;
 
   try
   {
      FileStream aFile = new FileStream("Temp.txt", FileMode.Create);
      charData = "My pink half of the drainpipe.".ToCharArray();
      byData = new byte[charData.Length];
      Encoder e = Encoding.UTF8.GetEncoder();
      e.GetBytes(charData, 0, charData.Length, byData, 0, true);
 
      // Move file pointer to beginning of file.
      aFile.Seek(0, SeekOrigin.Begin);
      aFile.Write(byData, 0, byData.Length);
   }
   catch (IOException ex)
   {
      Console.WriteLine("An IO exception has been thrown!");
      Console.WriteLine(ex.ToString());
      Console.ReadKey();
      return;
   }
}

(4) 運行該應用程式。稍後就將其關閉。

(5) 導航到應用程式目錄 —— 在目錄中已經儲存了檔案,因為我們使用了相對路徑。目錄位於WriteFile\bin\Debug檔案夾。開啟Temp.txt檔案。可以在檔案中看到如圖22-3所示的文本。

圖 22-3

樣本的說明
此應用程式在自己的目錄中開啟檔案,並在檔案中寫入了一個簡單的字串。在結構上這個樣本非常類似於前面的樣本,只是用Write()代替了Read(),用Encoder代替了Decoder。

下面的程式碼使用String類的ToCharArray()靜態方法,建立了字元數組。因為C#中的所有事物都是對象,文本“My pink half of the drainpipe.”實際上是一個String對象,所以甚至可以在字串上調用這些靜態方法。

複製代碼 代碼如下:

CharData = " My pink half of the drainpipe. ".ToCharArray();

下面的程式碼顯示了如何將字元數群組轉換為FileStream對象需要的正確位元組數組。

複製代碼 代碼如下:

Encoder e = Endoding.UTF8.GetEncoder();
e.GetBytes(charData,0,charData.Length, byData,0,true);

這次,要基於UTF8編碼方法來建立Encoder對象。也可以將Unicode用於解碼。這裡在寫入流之前,需要將字元資料編碼為正確的位元組格式。在GetBytes()方法中可以完成這些工作,它可以將字元數群組轉換為位元組數組,並將字元數組作為第一個參數(本例中的charData),將該數組中起始位置的下標作為第二個參數(0表示數組的開頭)。第三個參數是要轉換的字元數量(charData.Length,charData數組中的元素個數)。第四個參數是在其中置入資料的位元組數組(byData),第五個參數是在位元組數組中開始寫入位置的下標(0表示byData數組的開頭)。

最後一個參數決定在結束後Encoder對象是否應該更新其狀態,即Encoder對象是否仍然保留它原來在位元組數組中的記憶體位置。這有助於以後調用Encoder對象,但是當只進行單一調用時,這就沒有什麼意義。最後對Encoder的調用必須將此參數設定為true,以清空其記憶體,釋放對象,用於記憶體回收。

之後,使用Write()方法向FileStream寫入位元組數組就非常簡單:

複製代碼 代碼如下:

aFile.Seek(0,SeekOrigin.Begin);
aFile.Write(byData,0,byData.Length);

與Read()方法一樣,Write()方法也有三個參數:要寫入的數組,開始寫入的數組下標和要寫入的位元組數。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.