計算機以文件的形式把數據存儲在磁盤、光盤等存儲設備上。文件的管理和操做是操做系統的一個重要組成部分,.NET 框架提供了一組功能強大的類,能夠方便地對文件進行操做和管理。數組
1.文件操做相關的類網絡
用於文件操做的類位於System.IO 命名空間中,用這些類能夠方便地對文件進行建立、讀寫、複製、刪除、移動、打開等操做。框架
2.File類和FileInfo類異步
命名空間 System.IO 中的File 類用於對文件進行建立、打開、複製、移動、刪除、重命名等典型操做,並能獲取或設置文件的屬性信息。File 類中全部的方法都是靜態的,使用起來很是簡單,File 類的部分方法以下圖所示:函數
File類的方法使用起來很是方便:工具
using System;操作系統
using System.IO;設計
static void Main(string[] args)3d
{ string path = @"D:\test.txt";code
if(File.Exists(path)) //若是文件已經存在,則讀取文件內容
{ //讀取文件
string contents = File.ReadAllText(path);
Console.WriteLine("讀取文件:\n" + contents); }
else //若是文件不存在,則建立文件並寫入內容
{ string contents = "迫不得已花落去,\n 似曾相識燕歸來,\n 小園香徑獨徘徊。";
File.WriteAllText(path, contents); //寫入文件
Console.WriteLine("文件已寫入。" ); }
}
注意 WriteAllText()、WriteAllLines()和WriteAllBytes()方法都會覆蓋之前的文件,使用時要特別當心。要想在文件尾部追加新文本,請使用AppendAllText()方法。
FileInfo 類和File 類類似,能夠建立、複製、移動、刪除文件,能夠獲取或設置文件的屬性,只是少了讀寫文件的功能。FileInfo 類的成員方法都是非靜態的,使用方法前須要先建立一個FileInfo 類的對象,而後經過FileInfo 對象調用方法。當頻繁操做某文件時,使用FileInfo 類的效率會更高。
(1)關於文件的異常
操做文件時常常會發生異常,好比指定文件不存在,路徑無效,沒有權限等。關於文件的異常的方法如圖示,所以關於文件的操做通常要放在 try-catch 結構中,以處理可能發生的異常狀況。
try
{ string path = @"D:\test.txt";
//若是文件已經存在,則讀取文件內容
if(File.Exists(path))
{ //讀取文件
string contents = File.ReadAllText(path);
Console.WriteLine("讀取文件:\n" + contents);
}
else //若是文件不存在,則建立文件並寫入內容
{
string contents = "迫不得已花落去,\n 似曾相識燕歸來,\n 小園香徑獨徘徊。";
//寫入文件
File.WriteAllText(path, contents);
Console.WriteLine("文件已寫入。" );
}
}
catch (Exception e)
{ //異常處理
Console.WriteLine(e.Message);
}
3.Directory類和DirectoryInfo類
System.IO 命名空間中的Directory 類用於執行對目錄(文件夾)的操做,好比建立、移動、刪除、重命名等,也可經過它獲取或設置目錄的屬性。Directory 類的部分方法如圖所示,全部 Directory 類中的方法都是靜態的,使用起來很是方便。
using System;
using System.IO;
static void Main(string[] args)
{ try
{ string path = @"D:\Program Files\Windows Media Player";
if (Directory.Exists(path)
{ //獲取子目錄
string[] dirs = Directory.GetDirectories(path);
Console.WriteLine("子目錄:");
foreach (string dir in dirs) {
Console.WriteLine(dir); }
//獲取文件
string[] files = Directory.GetFiles(path);
Console.WriteLine("文件:");
foreach (string file in files) {Console.WriteLine(file);}
}
else{Console.WriteLine("目錄不存在");}
}
catch (Exception e)
{//異常處理Console.WriteLine(e.Message);}
}
操做結果所圖所示,DirectoryInfo 類和Directory 類功能類似,區別是DirectoryInfo 類的成員方法都是非靜態的,使用方法前須要先建立一個DirectoryInfo 類的對象。當頻繁操做某目錄時,使用DirectoryInfo 類的效率會更高。
4.path類
經過 System.IO 命名空間中的Path 類,咱們能夠方便的處理路徑。Path 類的部分字段和方法如圖所示。Path 類中的不少字段是和操做系統關聯的,在不一樣的操做系統中可能有不一樣的結果。好比DirectorySeparatorChar,在Windows 和Macintosh 操做系統中的值是「\」,在Unix 操做系統中爲「/」;VolumeSeparatorChar 在Windows 和Macintosh 操做系統中爲「:」,在Unix操做系統中爲「/」;PathSeparator 在 Windows 操做系統中默認值是「;」,在 Unix 操做系統上爲「:」。
using System.IO;
//輸出分隔符
Console.WriteLine("DirectorySeparatorChar" + Path.DirectorySeparatorChar);
Console.WriteLine(" PathSeparator" + Path.PathSeparator);
Console.WriteLine(" VolumeSeparatorChar" + Path.VolumeSeparatorChar);
//輸出路徑信息
string path = @"D:\Program Files\Windows Media Player\wmplayer.exe";
Console.WriteLine(" GetFileName" + Path.GetFileName(path));
Console.WriteLine(" GetFileNameWithoutExt" + Path.GetFileNameWithoutExtension(path));
Console.WriteLine(" GetExtension" + Path.GetExtension(path));
Console.WriteLine(" GetDirectoryName" + Path.GetDirectoryName(path));
結果如圖所示:
(1)Environment類
經過 System 命名空間中的Environment 類,能夠方便地獲取與系統相關的信息。Environment 類的部分屬性和方法如圖所示。
一個例子:
//顯示系統信息
Console.WriteLine(" 處理器數量: " + Environment.ProcessorCount);
Console.WriteLine(" 操做系統版本: " + Environment.OSVersion);
Console.WriteLine("公共語言運行庫版本: " + Environment.Version);
Console.WriteLine(" 系統目錄: " + Environment.SystemDirectory);
Console.WriteLine(" 用戶域名: " + Environment.UserDomainName);
Console.WriteLine(" 用戶名: " + Environment.UserName);
Console.WriteLine(" 機器名: " + Environment.MachineName);
經過 Environment 類的GetFolderPath()方法能夠獲取系統特殊文件夾的路徑,這些特殊文件夾由SpecialFolder 枚舉列出。SpecialFolder 類的部分紅員如圖所示。
例子:
string d = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string p = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string m = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
string f = Environment.GetFolderPath(Environment.SpecialFolder.Favorites);
//輸出特殊目錄路徑
Console.WriteLine("個人文檔:" + d);
Console.WriteLine("個人圖片:" + p);
Console.WriteLine("個人音樂:" + m);
Console.WriteLine("個人收藏:" + f);
5.基於流的文件操做
數據以文件的形式存儲在硬盤、光盤等存儲介質上,讀寫數據的過程能夠看做數據像水同樣流入或流出存儲介質,因此.NET 設計了一種叫作流(Stream)的類來讀寫文件。實際上一旦咱們打開一個文件,它就和一個流關聯起來。文件是數據源,流是傳輸數據的工具,經過這種專門的工具來傳輸數據,就可使傳輸工具和數據源分離開來,從而更容易切換數據源,更容易實現不一樣環境下代碼的重用。數據源除了文件之外,還能夠是內存中數據、網絡上的數據甚至是代碼中的一個變量等等。與流相關的類都定義在 System.IO 命名空間中,它們大多數繼承於抽象類Stream。如圖展現了部分與流相關的類。
FileStream 類用於字節數據輸入和輸出,TextReader 和TextWriter 用於Unicode 字符的輸入和輸出,BinaryReader 和BinaryWriter 用於二進制數據的輸入和輸出。
(1)FileStream類
FileStream 是專門進行文件操做的流,使用它可對文件進行讀取、寫入、打開和關閉等操做,既支持同步讀寫操做,也支持異步(緩衝)讀寫操做。FileStream 類的部分屬性和方法如圖所示:
要使用流,必須先建立一個流的對象。請建立一個名爲「StreamTest」的控制檯項目,並添加以下代碼。
string fileName = @"D:\filestream_test.data";
//若文件不存在,則建立文件,寫入數據
if(!File.Exists(fileName))
{ FileInfo myFile = new FileInfo(fileName); //建立文件
FileStream fs = myFile.OpenWrite(); //獲取與文件對應的流
byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };
fs.Write(datas, 0, datas.Length); //用流寫入數據
Console.WriteLine("數據已寫入。");
fs.Close(); //關閉流
}
else //若文件存在,則讀取數據
{ //創建與文件關聯的流
FileStream fs = new FileStream(fileName, FileMode.Open, File Access.Read);
byte[] datas = new byte[fs.Length];
fs.Read(datas, 0, datas.Length); //用流讀取數據
Console.WriteLine(「讀取數據:」);
foreach(byte data in datas)
{ Console.Write(data);}
fs.Close(); //關閉流
}
這裏咱們用兩種方法獲取與文件對應的流。第一種辦法是經過FileInfo類的OpenWrite()方法獲取,OpenWrite()方法打開相應的文件,返回一個與該文件相關的FileStream對象。
當使用FileStream類的構造函數時,須要提供打開方式(FileMode)、訪問模式(FileAccess)、共享模式(FileShare)等信息,它們分別用FileMode枚舉、FileAccess枚舉和FileShare枚舉表示,這些枚舉的值很容易理解,更詳細的信息請參看幫助文檔。與FileStream類相關的枚舉如圖所示:
FileStream類的Write()方法和Read()方法。Write()方法的原型以下,參數中字節數組array 是數據源,offset 和count 表示把數據源中的從位置offset 開始的count 個字節寫入文件。
Read()方法的原型以下:參數中字節數組array 是目標數組,用於存儲讀取到的數據,存儲的位置是從位置offset開始的count 個字節。
返回值爲實際讀取到的字節數。若是開始讀取時當前位置已在流的末尾,則返回值爲0。若是流中當前可用的字節數沒有請求的字節數那麼多,則實際讀取到的字節數會小於請求的字節數。還有一個須要注意的狀況是,在處理網絡流(如FTP)、設備流(如串口輸入)等流時,即便還沒有到達流的末尾,實際讀取到的字節數仍有可能少於請求的字節數。若是是從控制檯讀取或者從本地文件讀取,不多遇到這種狀況。
綜上能夠看出,FileStream 類主要用於向文件中寫入或讀取以字節爲單位的數據。
(2)關於流的異常
try-catch-fianlly結構,用流來操做文件也可能會出現異常,因此關於流的操做通常要放在try-catch-fianlly 結構中,以加強程序的健壯性。
string fileName = @"D:\filestream_test.data";
FileStream fs = null;
try
{…}
catch(Exception e)
{ Console.WriteLine(e.Message);}
finally
{ if (fs!=null) fs.Close(); }
注意,fs對象的聲明代碼要放在try語句以前,否則catch塊和fianly塊中沒法識別。另外關閉流的操做應放在fianlly塊中,以確保不管操做成功仍是操做失敗,流都被及時釋放。
讀取一個文件的內容時,真正有用的代碼每每只有幾行,然而爲了健壯性,咱們卻要寫上10多行的代碼來處理異常和關閉文件。如何才能減小這種麻煩呢?一種辦法是本身編寫一個方法,把異常處理和關閉文件的代碼封裝進去。下面編寫了一個處理文件的通用方法UniversalProcess(),只要告訴它文件路徑和處理文件的具體代碼,它就會自動完成異常處理和關閉文件的操做。
public delegate void MyFileProcessCode(FileStream file);
//封裝文件處理
public static void UniversalProcess(string path,MyFileProcessCode doSomething)
{ FileStream fs=new FileStream(path,FileMode.Open,FileAccess.ReadWrite);
try { doSomething(fs);}
catch(Exception e) {Console.WriteLine(e.Message);}
finally{if(fs!=null) fs.Close();}
}
能夠直接經過匿名函數和一個文件地址進行訪問,例如:
UniversalProcess(@"D:\a.text", delegate(FileStream fs)
{
byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };
fs.Write(datas, 0, datas.Length);
});
6.using語句
爲了防止出現同步問題,當一個程序讀寫文件時,操做系統一般都會阻止其餘程序讀寫該文件,所以使用完畢後要及時關閉,不然就會致使其餘程序不能使用該文件。除了能夠在fianlly塊中關閉文件(流)外,咱們還能夠用C#提供的using語句進行文件操做。
using(FileStream fs = File.OpenWrite(path))
{
byte[] datas = { 100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };
fs.Write(datas, 0, datas.Length);
}
在上面的代碼在using後的括號裏建立了一個流對象fs,而後在using後的語句塊中使用該流對象。當退出using語句時,系統會自動關閉流對象fs,使用起來很是方便。using語句適用於那些須要及時釋放資源的代碼,其通常形式爲:
using(type obj = initialization)
{ //具體處理代碼}
在using後的括號中建立一個對象obj,而後在後面的大括號中使用它。當退出using語句時,對象obj會被及時銷燬。using語句其實是對try語句的封裝,等價於:
{ type obj=initialization;
try {//具體處理代碼}
finally{ if (obj!=null) { ((IDisposable)obj).Dispose();}}
}
程序先執行try塊中的代碼,不論是否發生異常,系統都會自動執行finally塊中的代碼,調用obj對象的Dispose()方法,銷燬對象,釋放資源。Dispose()方法和Close()方法的功能基本上是同樣的,都是用來銷燬對象。實際上,Close()方法就是經過調用Dispose(),方法實現的。
由此看出 using 語句不光適用於處理關於流的操做,它也適用於任何包含Dispose()方法且須要及時銷燬的的對象。using 語句使用起來很是方便,若是你查看幫助文檔,就會發現涉及流的例子都是用using 語句編寫的。
7.用流讀寫文本文件
文本文件主要經過 StreamReader 和StreamWriter 來實現讀寫,它們分別繼承於抽象類TextReader 和TextWriter。StreamReader 類的部分屬性和方法,StreamWriter 類的部分屬性和方法,分別所圖示。
要使用 StreamReader 和StreamWriter,也必須先建立它們的對象。
StreamWriter streamWriter = null;
StreamReader streamReader = null;
try
{
//若文件不存在,則建立文件,寫入數據
if(!File.Exists(@"D:\text_test.txt"))
{
FileInfo myFile = new FileInfo(@"D:\text_test.txt"); //建立文件
streamWriter = myFile.CreateText(); //獲取文件對應的流
string text = @"何處望神州,滿眼風光北固樓。千古興亡多少事,悠悠。不盡長江滾滾流。年少萬兜鍪,坐斷東南戰未休。天下英雄誰敵手?曹劉。生子當如孫仲謀。";
streamWriter.Write(text); //用流寫入數據
Console.WriteLine("數據已寫入.");
}
else//若文件存在,則讀取數據
{ //創建與文件關聯的流
streamReader = new StreamReader(@"D:\text_test.txt");
string text = streamReader.ReadToEnd(); //用流讀取數據
Console.WriteLine("讀取數據:\n" + text);
}
}
catch (Exception e)
{ //異常處理
Console.WriteLine(e.Message);
}
finally
{ //關閉流
if(streamWriter!=null)streamWriter.Close();
if(streamReader!=null)streamReader.Close();
}
這裏咱們用兩種方法獲取與文件對應的流。第一種辦法是經過FileInfo 類(或File 類)的方法獲取,好比FileInfo 對象myFile的CreateText()方法返回一個StreamWriter 流:StreamWriter streamWriter =myFile.CreateText(); OpenText ()方法返回StreamReader 流:StreamReader streamReader=myFile.ReadText(); 。File 類的部分與流相關的方法如圖所示。
第二種辦法是用 StreamReader 類或StreamWriter 類的構造函數建立流。
StreamReader streamReader = new StreamReader(fileName);
8.用流讀寫二進制文件
經過 BinryReader 和BinaryWriter 類實現二進制讀寫。BinryReader 類的部分屬性和方法,BinaryWriter 類的部分屬性和方法,分別如圖所示:
要使用 BinryReader 和BinaryWriter 類,也必須先建立它們的對象,但它們的對象老是經過FileStream 對象來建立的。
string path = @"D:\binary_test.txt";
FileStream fileStream = null;
try
{ //建立文件
fileStream = File.Create(path);
//寫文件
BinaryWriter bw = new BinaryWriter(fileStream);
DateTime time = DateTime.Now;
bw.Write(time.Year);
bw.Write(time.Month);
bw.Write(time.Day);
bw.Write(time.Hour);
bw.Write(time.Minute);
bw.Write(time.Second);
//當前位置移動到開頭
fileStream.Seek(0, SeekOrigin.Begin);
//讀文件
BinaryReader br = new BinaryReader(fileStream);
int year = br.ReadInt32();
int month = br.ReadInt32();
int day = br.ReadInt32();
int hour = br.ReadInt32();
int minute = br.ReadInt32();
int second = br.ReadInt32();
Console.WriteLine("{0}-{1}-{2} {3}:{4}:{5}",
year, month, day, hour, minute, second);
}
catch (Exception e)
{
//異常處理
Console.WriteLine(e.Message);
}
finally
{
//釋放資源
if(fileStream!=null)
fileStream.Close();
}
實際上,BinryReader 和BinaryWriter 自己並非獨立的流,它們是對其餘流的包裝,因此建立BinryReader 和BinaryWriter 對象時,須要傳給它們一個FileStream 對象,對BinryReader 和BinaryWriter 的操做,就是對FileStream 的操做。