路径,文件,目录,I/O常见操作汇总
??? 文件操作是程序中非常基礎(chǔ)和重要的內(nèi)容,而路徑、文件、目錄以及I/O都是在進(jìn)行文件操作時(shí)的常見主題,這里想把這些常見的問題作個(gè)總結(jié),對于每個(gè)問題,盡量提供一些解決方案,即使沒有你想要的答案,也希望能提供給你一點(diǎn)有益的思路,如果你有好的建議,懇請能夠留言,使這些內(nèi)容更加完善。
??? 主要內(nèi)容:
??? 一、路徑的相關(guān)操作, 如判斷路徑是否合法,路徑類型,路徑的特定部分,合并路徑,系統(tǒng)文件夾路徑等內(nèi)容;
??? 二、相關(guān)通用文件對話框,這些對話框可以幫助我們操作文件系統(tǒng)中的文件和目錄;
??? 三、文件、目錄、驅(qū)動器的操作,如獲取它們的基本信息,獲取和設(shè)置文件和目錄的屬性,文件的版本信息,
??????? 搜索文件和目錄,文件判等,復(fù)制、移動、刪除、重命名文件和目錄;
??? 四、讀寫文件,包括臨時(shí)文件,隨機(jī)文件名等;
??? 五、對文件系統(tǒng)的監(jiān)視;
??? 這一篇就先寫一下前兩部分。
??? 一、路徑相關(guān)操作
??? 問題1: 如何判定一個(gè)給定的路徑是否有效/合法;
??? 解決方案:通過Path.GetInvalidPathChars或Path.GetInvalidFileNameChars方法獲得非法的路徑/文件名字符,可以
??? 根據(jù)它來判斷路徑中是否包含非法字符;
??? 問題2: 如何確定一個(gè)路徑字符串是表示目錄還是文件;
??? 解決方案:
??? 1、使用Directory.Exists或File.Exist方法,如果前者為真,則路徑表示目錄;如果后者為真,則路徑表示文件;
??? 2、上面的方法有個(gè)缺點(diǎn)就是不能處理那些不存在的文件或目錄。這時(shí)可以考慮使用Path.GetFileName方法獲得
?????? 其包含的文件名,如果一個(gè)路徑不為空,而文件名為空那么它表示目錄,否則表示文件;
??? 問題3: 如何獲得路徑的某個(gè)特定部分(如文件名、擴(kuò)展名等);
??? 解決方案:
??? 下面是幾個(gè)相關(guān)方法:
??? Path.GetDirectoryName :返回指定路徑字符串的目錄信息;
??? Path.GetExtension : 返回指定的路徑字符串的擴(kuò)展名;
??? Path.GetFileName : 返回指定路徑字符串的文件名和擴(kuò)展名;
??? Path.GetFileNameWithoutExtension :返回不具有擴(kuò)展名的路徑字符串的文件名;
??? Path.GetPathRoot :獲取指定路徑的根目錄信息;
??? (更多內(nèi)容還請參考MSDN)
??? 問題4: 如何準(zhǔn)確地合并兩個(gè)路徑而不用去擔(dān)心那個(gè)煩人的”/”字符;
??? 解決方案:
??? 使用Path.Combine方法,它會幫你處理煩人的”/”;
??? 問題5:如何獲得系統(tǒng)目錄的的路徑(如桌面,我的文檔,臨時(shí)文件夾等);
??? 解決方案:
??? 主要是使用System. Environment類的相關(guān)屬性和方法:
??? Environment. SystemDirectory屬性:獲取系統(tǒng)目錄的完全限定路徑;
??? Environment. GetFolderPath方法:該方法接受的參數(shù)類型為Environment.SpecialFolder枚舉,
??? 通過這個(gè)方法可以獲得大量系統(tǒng)文件夾的路徑,如我的電腦,我的電腦,桌面,系統(tǒng)目錄等;
??? (更多內(nèi)容還請參考MSDN);
??? Path.GetTempPath方法:返回當(dāng)前系統(tǒng)的臨時(shí)文件夾的路徑;
??? 問題6:如何判斷一個(gè)路徑是絕對路徑還是相對路徑;
??? 解決方案:
??? 使用Path.IsPathRooted方法;
??? 問題7:如何讀取或設(shè)置當(dāng)前目錄;
??? 解決方案:
??? 使用Directory類的GetCurrentDirectory和SetCurrentDirectory方法;
??? 問題8:如何使用相對路徑;
??? 解決方案:
??? 設(shè)置當(dāng)前目錄后(見問題7),就可以使用相對路徑了。對于一個(gè)相對路徑,我們可以
??? 使用Path.GetFullPath方法獲得它的完全限定路徑(絕對路徑)。
??? 注意:如果打算使用相對路徑,建議你將工作目錄設(shè)置為各個(gè)交互文件的共同起點(diǎn),否則可能會引入
??? 一些不易發(fā)現(xiàn)的安全隱患,被惡意用戶利用來訪問系統(tǒng)文件。
??? 更多內(nèi)容:
??? 通常我們可以使用System.IO.Path類來處理路徑。該類提供了一套方法和屬性用于對包含文件或目錄路徑信息的字符串執(zhí)行操作,這些操作是以跨平臺的方式執(zhí)行的,而這些方法和屬性都是靜態(tài)的。
??? 注意路徑僅僅是提供文件或目錄位置的字符串。路徑不必指向磁盤上的位置,例如,路徑可以映射到內(nèi)存中或設(shè)備上的位置。路徑的準(zhǔn)確格式是由當(dāng)前平臺確定的。例如,在某些系統(tǒng)上,路徑可以驅(qū)動器號或卷號開始,而此元素在其他系統(tǒng)中是不存在的。在某些系統(tǒng)上,文件路徑可以包含擴(kuò)展名,擴(kuò)展名指示在文件中存儲的信息的類型。文件擴(kuò)展名的格式是與平臺相關(guān)的;例如,某些系統(tǒng)將擴(kuò)展名的長度限制為 3 個(gè)字符,而其他系統(tǒng)則沒有這樣的限制。當(dāng)前平臺還確定用于分隔路徑中各元素的字符集,以及確定在指定路徑時(shí)不能使用的字符集。因?yàn)檫@些差異,所以 Path 類的字段以及 Path 類的某些成員的準(zhǔn)確行為是與平臺相關(guān)的。
??? 路徑可以包含絕對或相對位置信息。絕對路徑完整指定一個(gè)位置:文件或目錄可被唯一標(biāo)識,而與當(dāng)前位置無關(guān)。相對路徑指定部分位置:當(dāng)定位用相對路徑指定的文件時(shí),當(dāng)前位置用作起始點(diǎn)。
??? Path類的大多數(shù)成員不與文件系統(tǒng)交互,并且不驗(yàn)證路徑字符串指定的文件是否存在。修改路徑字符串的Path 類成員(例如 ChangeExtension)對文件系統(tǒng)中文件的名稱沒有影響。但Path成員確實(shí)驗(yàn)證指定路徑字符串的內(nèi)容;并且如果字符串包含在路徑字符串中無效的字符(如 InvalidPathChars 中的定義),則引發(fā) ArgumentException異常。例如,在基于 Windows 的桌面平臺上,無效路徑字符可能包括引號 (")、小于號 (<)、大于號 (>)、管道符號 (|)、退格 (/b)、空 (/0) 以及從 16 到 18 和從 20 到 25的 Unicode 字符。
??? Path 類的成員使您可以快速方便地執(zhí)行常見操作,例如確定文件擴(kuò)展名是否是路徑的一部分,以及將兩個(gè)字符串組合成一個(gè)路徑名。
??? 多數(shù)情況下,如果這些方法接收了無效的路徑會拋出異常,但如果路徑名是因?yàn)榘送ㄅ浞?#xff08;*或?)從而無效,則不會拋出異常(可以使用GetInvalidPathChars方法來非法的路徑字符)。我們可以根據(jù)該原則判斷一個(gè)路徑是否合法。? ?? 二、相關(guān)的通用文件對話框???
??? 1、文件夾瀏覽對話框(FolderBrowserDialog類)
??? 用戶可以通過該對話框?yàn)g覽、新建并選擇文件夾?
??? 主要屬性:
??? Description:樹視圖控件上顯示的說明文本,如上圖中的”選擇要進(jìn)行計(jì)算的目錄”;
??? RootFolder:獲取或設(shè)置從其開始瀏覽的根文件夾,如上圖中設(shè)置的我的電腦(默認(rèn)為桌面);
??? SelectedPath:獲取或設(shè)置用戶選定的路徑,如果設(shè)置了該屬性,打開對話框時(shí)會定位到指定路徑,默認(rèn)為根文件夾,關(guān)閉對話框時(shí)根據(jù)該屬性獲取用戶用戶選定的路徑;
??? ShowNewFolderButton:獲取或設(shè)置是否顯示新建對話框按鈕;
??? 主要方法:
??? ShowDialog:打開該對話框,返回值為DialogResult類型值,如果為DialogResult.OK,則可以由SelectedPath屬性獲取用戶選定的路徑;
????dlgOpenFolder.RootFolder?=?Environment.SpecialFolder.MyComputer;
????dlgOpenFolder.ShowNewFolderButton?=?true;
????DialogResult?result?=?dlgOpenFolder.ShowDialog(this);
????if?(result?==?DialogResult.OK)
????{
????????txtDirPath.Text?=?dlgOpenFolder.SelectedPath;
????}
?
??? 2、打開文件對話框(OpenFileDialog類)
??? 用戶可以通過該對話框選擇一個(gè)文件
???
??? CheckFileExists:該值指示如果用戶指定不存在的文件名,對話框是否顯示警告;
??? FileName(s):獲取或設(shè)置一個(gè)包含在文件對話框中選定的文件名的字符串;
??? Filter:獲取或設(shè)置對話框的文件類型列表;
??? FilterIndex:對話框的文件類型列表的索引(基于1的);
??? InitialDirectory:獲取或設(shè)置文件對話框顯示的初始目錄;
??? Multiselect:該值指示對話框是否允許選擇多個(gè)文件;
??? ShowReadOnly:該值指示對話框是否包含只讀復(fù)選框;
??? Title:獲取或設(shè)置文件對話框標(biāo)題;
??? 主要方法:
??? OpenFile:打開用戶選定的具有 只讀權(quán)限的文件;
??? ShowDialog:打開該模式對話框;
????dlgOpenFile.Title?=?"打開源文件";
????dlgOpenFile.InitialDirectory?=?@"C:/Inetpub/";
????dlgOpenFile.Filter?=?"文本文件?(*.txt)|*.txt|所有文件?(*.*)|*.*";
????dlgOpenFile.FilterIndex?=?2;
????dlgOpenFile.ShowReadOnly?=?true;
????DialogResult?dr?=?dlgOpenFile.ShowDialog();
????if?(dr?==?DialogResult.OK)
????{
??????? string fileName = dlgOpenFile.FileName;
????}
??? 3、保存文件對話框(SaveFileDialog類)
??? 用戶可以通過該對話框保存一個(gè)文件
???
??? 主要屬性:
??? 大部分與打開文件對話框類似,此處略過,下面幾個(gè)值得注意:
??? CreatePrompt:該值指示如果用戶指定不存在的文件,是否提示用戶允許創(chuàng)建該文件;
??? OverwritePrompt:該值指示如果用戶指定的文件名已存在,對話框是否顯示警告;
??? 主要方法:
??? OpenFile:打開用戶選定的具有讀/寫權(quán)限的文件;
??? ShowDialog:打開該模式對話框;
??? 示例代碼:
????dlgSaveFile.InitialDirectory?=?@"C:/Inetpub/";
????dlgSaveFile.Filter?=?"文本文件?(*.txt)|*.txt|所有文件?(*.*)|*.*";
????dlgSaveFile.FilterIndex?=?2;
????DialogResult?dr?=?dlgSaveFile.ShowDialog();
????if?(dr?==?DialogResult.OK)
????{
??????? string fileName = dlgSaveFile.FileName;
????}
??? 至此,我們操作的都只是路徑,要知道,這些路徑僅僅是字符串,還沒有涉及到文件系統(tǒng)中的真實(shí)文件。?
三、文件和目錄相關(guān)操作
??? 文件和目錄操作涉及的類主要是:FileInfo,DirectoryInfo,DriveInfo,可以認(rèn)為它們的一個(gè)實(shí)例對應(yīng)著一個(gè)文件、目錄、驅(qū)動器。它們的用法類似,一般是將文件、目錄或驅(qū)動器的路徑作為參數(shù)傳遞給相應(yīng)的構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例,然后訪問它們的屬性和方法。
??? 注意下面幾點(diǎn):
??? FileInfo 類和 DirectoryInfo 類都繼承自抽象類 FileSystemInfo , FileSystemInfo 類定義了一些通用的屬性,如 CreationTime 、 Exists 等。但 DriveInfo 類沒有繼承 FileSystemInfo 類,所以它也就沒有上面提到的那些通用屬性了。
??? FileInfo 類和 DirectoryInfo 類的對象公開的屬性值都是第一次查詢時(shí)獲取的值,如果在以此查詢之后文件或目錄發(fā)生了改動,就必須調(diào)用它們的 Refresh 方法來更新這些屬性。但 DriveInfo 則無需這么做,它的屬性每次都會讀取文件系統(tǒng)最新的信息。
??? 在創(chuàng)建文件、目錄或驅(qū)動器的實(shí)例時(shí),如果使用了一個(gè)不存在的路徑,并不會報(bào)錯(cuò),這是你得到一個(gè)對象,該對象表示一個(gè)并不存在的實(shí)體,這意味著它的 Exists 屬性(對于 DriveInfo 來說是 IsReady 屬性)值為 false 。你仍然可以操作該實(shí)體,但如果嘗試其它的大多數(shù)屬性,就會引發(fā)相應(yīng)的 FileNotFoundException 、 DirectoryNotFoundException 或 DriveNotFoundException 異常。
??? 另外,還可以使用 File / Directory 類,這兩個(gè)類的成員都是靜態(tài)方法,所以如果只想執(zhí)行一個(gè)操作,那么使用 File/Directory 中的靜態(tài)方法的效率比使用相應(yīng)的 FileInfo / DirectoryInfo中的 實(shí)例方法可能更高。所有的 File / Directory 方法都要求當(dāng)前所操作的文件 / 目錄的路徑。 注意: File / Directory 類的靜態(tài)方法對所有方法都執(zhí)行安全檢查。如果打算多次重用某個(gè)對象,可考慮改用 FileInfo / DirectoryInfo 的相應(yīng)實(shí)例方法,因?yàn)椴⒉豢偸切枰踩珯z查。 ?
??? 下面是一些常見的問題:
??? 問題1:如何獲取指定文件的基本信息;
??? 解決方案:可以使用FileInfo類的相關(guān)屬性:
??? FileInfo.Exists:獲取指定文件是否存在;
??? FileInfo.Name,FileInfo.Extensioin:獲取文件的名稱和擴(kuò)展名;
??? FileInfo.FullName:獲取文件的全限定名稱(完整路徑);
??? FileInfo.Directory:獲取文件所在目錄,返回類型為DirectoryInfo;
??? FileInfo.DirectoryName:獲取文件所在目錄的路徑(完整路徑);
??? FileInfo.Length:獲取文件的大小(字節(jié)數(shù));
??? FileInfo.IsReadOnly:獲取文件是否只讀;
??? FileInfo.Attributes:獲取或設(shè)置指定文件的屬性,返回類型為FileAttributes枚舉,可以是多個(gè)值的組合(見問題2);
??? FileInfo.CreationTime、FileInfo.LastAccessTime、FileInfo.LastWriteTime:分別用于獲取文件的創(chuàng)建時(shí)間、訪問時(shí)間、修改時(shí)間;
??? (更多內(nèi)容還請參考MSDN)
??? 問題2:如何獲取和設(shè)置文件的屬性,比如只讀、存檔、隱藏等;
??? 解決方案:
??? 使用FileInfo.Attributes屬性可以獲取和設(shè)置文件的屬性,該屬性類型為FileAttributes枚舉,該枚舉的每個(gè)值表示一種屬性,FileAttributes枚舉具有屬性(Attribute)FlagsAttribute,所以該枚舉的值可以進(jìn)行組合,也就是一個(gè)文件可以同時(shí)擁有多個(gè)屬性。下面看看具體的做法:
??? 獲取屬性,比如判斷一個(gè)文件是否是只讀的:
????if?(file.Attributes?==?FileAttributes.ReadOnly)
????{
????????chkReadonly.Checked?=?true;
????}
????//?這種寫法就不會有問題了,它只檢查只讀屬性
????if?((file.Attributes?&?FileAttributes.ReadOnly)?==?FileAttributes.ReadOnly)
????{
????????chkReadonly.Checked?=?true;
????}
??? 設(shè)置屬性,比如添加和移除一個(gè)文件的只讀屬性:
????{
????????//?添加只讀屬性
????????file.Attributes?|=?FileAttributes.ReadOnly;
????}
????else
????{
????????//?移除只讀屬性
????????file.Attributes?&=?~FileAttributes.ReadOnly;
????}
??? 問題3:如何獲取文件的版本信息(比如版本號,版權(quán)聲明,公司名稱等);
??? 解決方案:
??? 使用FileVersionInfo類,該類有大量的版本信息相關(guān)的屬性。通過它的靜態(tài)方法GetVersionInfo獲得該類的一個(gè)實(shí)例,然后就可以訪問指定文件的版本信息了,非常方便。如FileVersion表示文件版本號,LegalCopyright表示指定文件的版權(quán)聲明,CompanyName表示指定文件的公司名稱。(更多內(nèi)容還請參考MSDN)
??? 問題4:如何判斷兩個(gè)文件的內(nèi)容是否相同(精確匹配);
??? 解決方案:
??? 使用System.security.Cryptography.HashAlgorithm類為每個(gè)文件生成一個(gè)哈希碼,然后比較兩個(gè)哈希碼是否一致。
??? 在比較文件內(nèi)容的時(shí)候可以采用好幾種方法。例如,檢查文件的某一特定部分是否一致;如果愿意,你甚至可以逐字節(jié)讀取文件,逐字節(jié)進(jìn)行比較。這兩種方法都是可以的,但在某些情況下,還是使用哈希碼算法更為方便。
??? 該算法為一個(gè)文件生成一個(gè)小的(通常約為20字節(jié))二進(jìn)制”指紋”(binary fingerprint)。從統(tǒng)計(jì)學(xué)角度看,不同的文件不可能生成相同的哈希碼。事實(shí)上,即使是一個(gè)很小的改動(比如,修改了源文件中的一個(gè)bit),也會有50%的幾率來改變哈希碼中的每一個(gè)bit。因此,哈希碼常常用于數(shù)據(jù)安全方面。
??? 要生成一個(gè)哈希碼,你必須首先創(chuàng)建一個(gè)HashAlgorithm對象,而這通常是調(diào)用HashAlgorithm.Create方法來完成的;然后調(diào)用HashAlgorithm.ComputeHash方法,它會返回一個(gè)存儲哈希碼的字節(jié)數(shù)組。代碼如下:
????///?判斷兩個(gè)文件內(nèi)容是否一致
????///?</summary>
????public?static?bool?IsFilesEqual(string?fileName1,?string?fileName2)
????{
????????using?(HashAlgorithm?hashAlg?=?HashAlgorithm.Create())
????????{
????????????using?(FileStream?fs1?=?new?FileStream(fileName1,?FileMode.Open),?fs2?=?new?FileStream(fileName2,?FileMode.Open))
????????????{
????????????????byte[]?hashBytes1?=?hashAlg.ComputeHash(fs1);
????????????????byte[]?hashBytes2?=?hashAlg.ComputeHash(fs2);
????????????????//?比較哈希碼
????????????????return?(BitConverter.ToString(hashBytes1)?==?BitConverter.ToString(hashBytes2));
????????????}
????????}
????}
??? 問題5:如何獲取指定目錄的基本信息;
??? 解決方案:可以使用DirectoryInfo類的相關(guān)屬性和方法:
??? DirectoryInfo.Exists:獲取指定目錄是否存在;
??? DirectoryInfo.Name:獲取目錄的名稱;
??? DirectoryInfo.FullName:獲取目錄的全限定名稱(完整路徑);
??? DirectoryInfo.Attributes:獲取或設(shè)置指定目錄的屬性,返回類型為FileAttributes枚舉,可以是多個(gè)值的組合;??
??? DirectoryInfo.CreationTime、FileInfo.LastAccessTime、FileInfo.LastWriteTime:分別用于獲取目錄的創(chuàng)建時(shí)間、訪問時(shí)間、修改時(shí)間;
??? DirectoryInfo.Parent:獲取目錄的上級目錄,返回類型為DirectoryInfo;
??? DirectoryInfo.Root:獲取目錄的根目錄,返回類型為DirectoryInfo;
??? 問題6:如何獲取指定目錄包含的文件和子目錄;
??? 解決方案:
??? DirectoryInfo.GetFiles():獲取目錄中(不包含子目錄)的文件,返回類型為FileInfo[],支持通配符查找;
??? DirectoryInfo.GetDirectories():獲取目錄(不包含子目錄)的子目錄,
??????? 返回類型為DirectoryInfo[],支持通配符查找;
??? DirectoryInfo. GetFileSystemInfos():獲取指定目錄下(不包含子目錄)的文件和子目錄,
??????? 返回類型為FileSystemInfo[],支持通配符查找;
??? 問題7:如何獲得指定目錄的大小;
??? 解決方案:
??? 檢查目錄內(nèi)的所有文件,利用FileInfo.Length屬性獲取每個(gè)文件的大小,然后進(jìn)行合計(jì),然后使用遞歸算法處理所有的子目錄的文件,參考下面代碼:
????///?計(jì)算一個(gè)目錄的大小
????///?</summary>
????///?<param?name="di">指定目錄</param>
????///?<param?name="includeSubDir">是否包含子目錄</param>
????///?<returns></returns>
????private?long?CalculateDirSize(DirectoryInfo?di,?bool?includeSubDir)
????{
????????long?totalSize?=?0;
????????//?檢查所有(直接)包含的文件
????????FileInfo[]?files?=?di.GetFiles();
????????foreach?(FileInfo?file?in?files)
????????{
????????????totalSize?+=?file.Length;
????????}
????????//?檢查所有子目錄,如果includeSubDir參數(shù)為true
????????if?(includeSubDir)
????????{
????????????DirectoryInfo[]?dirs?=?di.GetDirectories();
????????????foreach?(DirectoryInfo?dir?in?dirs)
????????????{
????????????????totalSize?+=?CalculateDirSize(dir,?includeSubDir);
????????????}
????????}
????????return?totalSize;
????}
??? 問題8:如何使用通配符搜索指定目錄內(nèi)的所有文件;
??? 解決方案:
??? 使用DirectoryInfo.GetFiles方法的重載版本,它可以接受一個(gè)過濾表達(dá)式,返回FileInfo數(shù)組,另外它的參數(shù)還可以指定是否對子目錄進(jìn)行查找。如:
??? 問題9:如何復(fù)制、移動、重命名、刪除文件和目錄;
??? 解決方案:使用FileInfo和DirectoryInfo類。
??? 下面是FileInfo類的相關(guān)方法:
??? FileInfo.CopyTo:將現(xiàn)有文件復(fù)制到新文件,其重載版本還允許覆蓋已存在文件;
??? FileInfo.MoveTo:將指定文件移到新位置,并提供指定新文件名的選項(xiàng),所以可以用來重命名文件(而不改變位置);??? FileInfo.Delete:永久刪除文件,如果文件不存在,則不執(zhí)行任何操作;
??? FileInfo.Replace:使用當(dāng)前FileInfo對象對應(yīng)文件的內(nèi)容替換目標(biāo)文件,而且指定另一個(gè)文件名作為被替換文件的備份,微軟考慮實(shí)在周到。
??? 下面是DirectoryInfo類的相關(guān)方法:
??? DirectoryInfo.Create:創(chuàng)建指定目錄,如果指定路徑中有多級目錄不存在,該方法會一一創(chuàng)建;
??? DirectoryInfo.CreateSubdirectory:創(chuàng)建當(dāng)前對象對應(yīng)的目錄的子目錄;
??? DirectoryInfo.MoveTo:將目錄(及其包含的內(nèi)容)移動至一個(gè)新的目錄,也可用來重命名目錄;
??? DirectoryInfo.Delete:刪除目錄(如果它存在的話)。如果要?jiǎng)h除一個(gè)包含子目錄的目錄,要使用它的重載版本,以指定遞歸刪除。
??? 注意到了沒有?DirectoryInfo類少了一個(gè)CopyTo方法,不過我們可以通過遞歸來實(shí)現(xiàn)這個(gè)功能:
????///?復(fù)制目錄到目標(biāo)目錄
????///?</summary>
????///?<param?name="source">源目錄</param>
????///?<param?name="destination">目標(biāo)目錄</param>
????public?static?void?CopyDirectory(DirectoryInfo?source,?DirectoryInfo?destination)
????{
????????//?如果兩個(gè)目錄相同,則無須復(fù)制
????????if?(destination.FullName.Equals(source.FullName))
????????{
????????????return;
????????}
????????//?如果目標(biāo)目錄不存在,創(chuàng)建它
????????if?(!destination.Exists)
????????{
????????????destination.Create();
????????}
????????//?復(fù)制所有文件
????????FileInfo[]?files?=?source.GetFiles();
????????foreach?(FileInfo?file?in?files)
????????{
????????????//?將文件復(fù)制到目標(biāo)目錄
????????????file.CopyTo(Path.Combine(destination.FullName,?file.Name),?true);
????????}
????????//?處理子目錄
????????DirectoryInfo[]?dirs?=?source.GetDirectories();
????????foreach?(DirectoryInfo?dir?in?dirs)
????????{
????????????string?destinationDir?=?Path.Combine(destination.FullName,?dir.Name);
????????????//?遞歸處理子目錄
????????????CopyDirectory(dir,?new?DirectoryInfo(destinationDir));
????????}
????}
?
??? 問題10:如何獲得計(jì)算機(jī)的所有邏輯驅(qū)動器;
??? 解決方案:使用DriveInfo類(需要.NET 2.0)
??? DriveInfo.GetDrives():獲得計(jì)算機(jī)的所有邏輯驅(qū)動器,返回類型為DriveInfo[];?
??? 問題11:如何獲取指定驅(qū)動器的信息;
??? 解決方案:
??? DriveInfo.Name:獲取驅(qū)動器的名稱(如C:/);
??? DriveInfo.DriveType:獲取驅(qū)動器的類型(如Fixed,CDRom,Removable,Network等);
??? DriveInfo.DriveFormat:獲取驅(qū)動器的格式(如NTFS,FAT32,CDFS,UDF等);
??? DriveInfo.IsReady:獲取驅(qū)動器是否已準(zhǔn)備好,比如CD是否已放入CD驅(qū)動器,如果驅(qū)動器沒有準(zhǔn)備好,訪問其信息會引發(fā)IOException類型異常;
??? DriveInfo.AvailableFreeSpace:獲取驅(qū)動器的可用空間;
??? DriveInfo.TotalFreeSpace:獲取驅(qū)動器總的可用空間,它與AvailableFreeSpace的不同在于AvailableFreeSpace會磁盤配額的設(shè)置;
??? DriveInfo.TotalSize:獲取驅(qū)動器總的空間;
??? DriveInfo.RootDirectory:獲得驅(qū)動器的根目錄(DirectoryInfo類型);
至此,我們已經(jīng)了解了文件和目錄相關(guān)的一些基本操作。
文件讀寫相關(guān)類介紹:
??? 文件讀寫操作涉及的類主要是:
??? MarshalByRefObject 類:允許在支持遠(yuǎn)程處理的應(yīng)用程序中跨應(yīng)用程序域邊界訪問對象;
??? BinaryReader 類:用特定的編碼將基元數(shù)據(jù)類型讀作二進(jìn)制值。
??? BinaryWriter 類: 以二進(jìn)制形式將基元類型寫入流,并支持用特定的編碼寫入字符串。
??? Stream 類: 提供字節(jié)序列的一般視圖。
??? FileStream類:公開以文件為主的 Stream,既支持同步讀寫操作,也支持異步讀寫操作。
??? MemoryStream 類:創(chuàng)建其支持存儲區(qū)為內(nèi)存的流。
??? BufferedStream 類:給另一流上的讀寫操作添加一個(gè)緩沖層。
??? TextReader 類:表示可讀取連續(xù)字符系列的閱讀器。
??? TextWriter 類:表示可以編寫一個(gè)有序字符系列的編寫器。
??? StreamReader 類:實(shí)現(xiàn)一個(gè) TextReader,使其以一種特定的編碼從字節(jié)流中讀取字符。
??? StreamWriter 類:實(shí)現(xiàn)一個(gè) TextWriter,使其以一種特定的編碼向流中寫入字符。
??? StringReader 類:實(shí)現(xiàn)從字符串進(jìn)行讀取的 TextReader。
??? StringWriter 類:實(shí)現(xiàn)一個(gè)用于將信息寫入字符串的 TextWriter。該信息存儲在基礎(chǔ)StringBuilder中。
??? 在使用它們之前最好能了解它們的繼承關(guān)系,有助于作出最合適的選擇:
??? 另外還要注意一下FileInfo和File類的一些方法,如Create,CreateText,Open等,有時(shí)也會帶來方便。
??? 這些類的內(nèi)容比較繁多,更多內(nèi)容還請參考MSDN。?
??? 下面是一些常見的問題及其解決方案:
??? 問題1:如何讀寫文本文件(并考慮不同的編碼類型);
??? 解決方案:
??? 創(chuàng)建一個(gè)FileStream對象用以引用該文件。要寫入文件,將FileStream對象封裝在StreamWriter對象中,使用其重載了的Write方法;要讀取文件,將FileStream對象封裝在StreamReader對象中,使用其Read或ReadLine方法;
??? .NET Framework允許通過StreamWriter和StreamReader類操作任何流來讀寫文本文件。當(dāng)使用StreamWriter類寫入數(shù)據(jù)時(shí),調(diào)用它的Write方法,該方法在重載后可以支持所有常見的C#數(shù)據(jù)類型,包括字符串、字符、整數(shù)、浮點(diǎn)數(shù)以及十進(jìn)制數(shù)等。但Write方法總會將的得到的數(shù)據(jù)轉(zhuǎn)換為文本,如果希望將這些文本轉(zhuǎn)換回原來的數(shù)據(jù)類型,應(yīng)使用WriteLine方法,以確保每個(gè)值都處于單獨(dú)的一行上。
??? 字符串的表現(xiàn)形式取決于你使用的編碼,最常見的編碼類型包括下面幾種:ASCII,UTF-16,UTF-7,UTF-8。
??? .NET Framework在System.Text命名空間中為每種編碼類型提供了一個(gè)類。在使用StreamWriter和StreamReader類時(shí),可以指定需要的編碼類型,或者使用默認(rèn)的UTF-8。
??? 而在讀取文本文件時(shí),則要使用StreamReader類的Read或ReadLine方法。Read方法讀取單個(gè)字符或者指定個(gè)數(shù)的字符,返回類型為字符或字符數(shù)組;ReadLine方法則返回包含整行內(nèi)容的字符串;ReadToEnd方法從當(dāng)前位置讀取至流的結(jié)尾。
??? (更多內(nèi)容還請參考MSDN)
??? 寫入文本文件的示例:
????{
????????//?創(chuàng)建一個(gè)StreamWriter對象,使用UTF-8編碼格式
????????using?(StreamWriter?writer?=?new?StreamWriter(fs,?Encoding.UTF8))
????????{
????????????//?分別寫入十進(jìn)制數(shù),字符串和字符類型的數(shù)據(jù)
????????????writer.WriteLine(123.45M);
????????????writer.WriteLine("String?Data");
????????????writer.WriteLine('A');
????????}
????}
???
??? 讀取文本文件的示例:
????using?(FileStream?fs?=?new?FileStream(fileName,?FileMode.Open))
????{
????????using?(StreamReader?reader?=?new?StreamReader(fs,?Encoding.UTF8))
????????{
????????????string?text?=?string.Empty;
????????????while(!reader.EndOfStream)
????????????{
????????????????text?=?reader.ReadLine();
????????????????txtMessage.Text?+=?text?+?Environment.NewLine;
????????????}
????????}
????}
??? 問題2:如何讀寫二進(jìn)制文件(使用強(qiáng)數(shù)據(jù)類型);
??? 解決方案:
??? 創(chuàng)建一個(gè)FileStream對象用以引用該文件。要寫入文件,將FileStream對象封裝在BinaryWriter對象中,使用其重載了的Write方法;要讀取文件,將FileStream對象封裝在BinaryReader對象中,使用相應(yīng)數(shù)據(jù)類型的Read方法。?????????????
??? .NET Framework允許通過BinaryWriter和BinaryReader類操作任何流來讀寫二進(jìn)制數(shù)據(jù)。當(dāng)使用BinaryWriter類寫入數(shù)據(jù)時(shí),調(diào)用它的Write方法,該方法在重載后可以支持所有常見的C#數(shù)據(jù)類型,包括字符串、字符、整數(shù)、浮點(diǎn)數(shù)以及十進(jìn)制數(shù)等,然后數(shù)據(jù)會被編碼為一系列字節(jié)寫入文件,也可以配置該過程中的編碼類型。
??? 在使用二進(jìn)制文件時(shí),一定要特別注意其中的數(shù)據(jù)類型。當(dāng)你讀取數(shù)據(jù)時(shí),一定要使用BinaryReader類的某種強(qiáng)類型的Read方法。例如,要讀取字符串,要使用ReadString方法。(BinaryWriter在寫入二進(jìn)制文件時(shí)總會記錄字符串的長度以避免任何可能的錯(cuò)誤)
??? 寫入文件的示例:
????{
????????using?(BinaryWriter?writer?=?new?BinaryWriter(fs))
????????{
????????????//?寫入十進(jìn)制數(shù),字符串和字符
????????????writer.Write(234.56M);
????????????writer.Write("String");
????????????writer.Write('!');
????????}
????}
??? 讀取文件的示例:
????using?(FileStream?fs?=?new?FileStream(fileName,?FileMode.Open))
????{
????????using?(StreamReader?sr?=?new?StreamReader(fs))
????????{
????????????MessageBox.Show("全部數(shù)據(jù):"?+?sr.ReadToEnd());
????????????fs.Position?=?0;
????????????using?(BinaryReader?reader?=?new?BinaryReader(fs))
????????????{
????????????????//?選用合適的數(shù)據(jù)類型讀取數(shù)據(jù)
????????????????string?message?=?reader.ReadDecimal().ToString()?+?Environment.NewLine;
????????????????message?+=?reader.ReadString()?+?Environment.NewLine;
????????????????message?+=?reader.ReadChar().ToString();
????????????????MessageBox.Show(message);
????????????}
????????}
????}
??? 問題3:如何異步讀取文件;
??? 解決方案:
??? 有時(shí)你需要讀取一個(gè)文件但又不希望影響程序的執(zhí)行。常見的情況是讀取一個(gè)存儲在網(wǎng)絡(luò)驅(qū)動器上的文件。?????????
??? FileStream提供了對異步操作的基本支持,即它的BeginRead和EndRead方法。使用這些方法,可以在.NET Framework線程池提供的線程中讀取一個(gè)數(shù)據(jù)塊,而無須直接與System.Threading命名空間中的線程類打交道。
??? 采用異步方式讀取文件時(shí),可以選擇每次讀取數(shù)據(jù)的大小。根據(jù)情況的不同,你可能會每次讀取很小的數(shù)據(jù)(比如,你要將數(shù)據(jù)逐塊拷貝至另一個(gè)文件),也可能是一個(gè)相對較大的數(shù)據(jù)(比如,在程序邏輯開始之前需要一定數(shù)量的數(shù)據(jù))。在調(diào)用BeginRead時(shí)指定要讀取數(shù)據(jù)塊的大小,同時(shí)傳入一個(gè)緩沖區(qū)(buffer)以存放數(shù)據(jù)。因?yàn)锽eginRead和EndRead需要訪問很多相同的信息,如FileStream,buffer,數(shù)據(jù)塊大小等,因此將這些內(nèi)容封裝一個(gè)單獨(dú)的類當(dāng)中是一個(gè)好主意。
??? 下面這個(gè)類就是一個(gè)簡單的示例。AsyncProcessor類提供了StartProcess方法,調(diào)用它開始讀取,每次讀取操作結(jié)束,OnCompletedRead回調(diào)函數(shù)會被觸發(fā),此時(shí)可以處理數(shù)據(jù),如果還有剩余數(shù)據(jù),則開始一個(gè)新的讀取操作。默認(rèn)情況下,AsyncProcessor類每次讀取2KB數(shù)據(jù)。
????{
????????private?Stream?inputStream;
????????//?每次讀取塊的大小
????????private?int?bufferSize?=?2048;
????????public?int?BufferSize
????????{
????????????get?{?return?bufferSize;?}
????????????set?{?bufferSize?=?value;?}
????????}
????????//?容納接收數(shù)據(jù)的緩存
????????private?byte[]?buffer;
????????public?AsyncProcessor(string?fileName)
????????{
????????????buffer?=?new?byte[bufferSize];
????????????//?打開文件,指定參數(shù)為true以提供對異步操作的支持
????????????inputStream?=?new?FileStream(fileName,?FileMode.Open,?FileAccess.Read,?FileShare.Read,?bufferSize,?true);
????????}
????????public?void?StartProcess()
????????{
????????????//?開始異步讀取文件,填充緩存區(qū)
????????????inputStream.BeginRead(buffer,?0,?buffer.Length,?OnCompletedRead,?null);
????????}
????????private?void?OnCompletedRead(IAsyncResult?asyncResult)
????????{
????????????//?已經(jīng)異步讀取一個(gè)?塊?,接收數(shù)據(jù)
????????????int?bytesRead?=?inputStream.EndRead(asyncResult);
????????????//?如果沒有讀取任何字節(jié),則流已達(dá)文件結(jié)尾
????????????if?(bytesRead?>?0)
????????????{
????????????????//?暫停以模擬對數(shù)據(jù)塊的處理
????????????????Debug.WriteLine("??異步線程:已讀取一塊");
????????????????Thread.Sleep(TimeSpan.FromMilliseconds(20));
????????????????//?開始讀取下一塊
????????????????inputStream.BeginRead(buffer,?0,?buffer.Length,?OnCompletedRead,?null);
????????????}
????????????else
????????????{
????????????????//?結(jié)束操作
????????????????Debug.WriteLine("??異步線程:讀取文件結(jié)束");
????????????????inputStream.Close();
????????????}
????????}
????}
? ? 使用該類時(shí)可以這么寫:
????AsyncProcessor?asyncIO?=?new?AsyncProcessor("test.txt");
????asyncIO.StartProcess();
????//?在主程序中,做其它事情,這里簡單地循環(huán)10秒
????DateTime?startTime?=?DateTime.Now;
????while?(DateTime.Now.Subtract(startTime).TotalSeconds?<?10)
????{
????????Debug.WriteLine("主程序:正在進(jìn)行");
????????//?暫停線程以模擬耗時(shí)的操作
????????Thread.Sleep(TimeSpan.FromMilliseconds(100));
????}
????Debug.WriteLine("主程序:已完成");
? ??
??? 問題4:如何創(chuàng)建臨時(shí)文件;
??? 解決方案:
??? 有時(shí)需要在特定用戶的臨時(shí)目錄下創(chuàng)建一個(gè)臨時(shí)文件,這要求該文件具有唯一的名稱,避免與其它程序生成的臨時(shí)文件相沖突。我們會有多種選擇。最簡單的是,在程序所在目錄內(nèi)使用GUID或時(shí)間戳加上隨機(jī)值作為文件名稱。但Path類提供的方法還是可以為你節(jié)省工作量,這就是它的靜態(tài)GetTempFileName方法,它在當(dāng)前用戶的臨時(shí)目錄下創(chuàng)建一個(gè)臨時(shí)文件(文件名稱一定是唯一的),臨時(shí)目錄通常類似于這樣:C:/Documents and Settings/[username]/Local Settings/Temp。
????using?(FileStream?fs?=?new?FileStream(tempFile,?FileMode.Open))
????{
????????using?(BinaryWriter?writer?=?new?BinaryWriter(fs))
????????{
????????????//?寫入數(shù)據(jù)
????????????writer.Write("臨時(shí)文件信息");
????????}
????}
????//?Do?something
????//?最后刪除臨時(shí)文件
????File.Delete(tempFile);
??? 問題5:如何獲得隨機(jī)文件名;
??? 解決方案:
??? 使用Path.GetRandomFileName方法,它與GetTempFileName方法的不同之處在于它僅僅返回一個(gè)字符串但不會創(chuàng)建文件。
??? 問題6:監(jiān)視文件系統(tǒng)的變化;
??? 解決方案:
??? 如果指定路徑內(nèi)的文件發(fā)生改變(比如文件被修改或創(chuàng)建),你希望能對此作出反應(yīng)。
??? 如果程序與其它多個(gè)程序或業(yè)務(wù)處理相關(guān),常常需要?jiǎng)?chuàng)建一個(gè)程序,并且只有文件系統(tǒng)發(fā)生變化時(shí)它才處于活動狀態(tài)。你可以創(chuàng)建一個(gè)這樣的程序,讓它定期區(qū)檢測指定目錄,此時(shí)會發(fā)現(xiàn)有件事情讓你苦惱:檢測得越頻繁,就會浪費(fèi)越多的系統(tǒng)資源;而檢測得越少,那么檢測到變化的時(shí)間就會越長。
??? 這時(shí)可以使用FileSystemWatcher組件,指定要進(jìn)行監(jiān)視的目錄或文件,并處理其Created,Deleted,Renamed,Changed事件。
??? 要使用FileSystemWatcher組件,首先要?jiǎng)?chuàng)建它的一個(gè)實(shí)例,然后設(shè)置下列屬性:
??? Path:指定要監(jiān)視的目錄;
??? Filter:指定要監(jiān)視的文件類型,如”*.txt”;
??? NotifyFilter:指定要監(jiān)視的變化類型;
??? FileSystemWatcher會引發(fā)四個(gè)關(guān)鍵的事件:Created,Deleted,Renamed,Changed。這些事件都在其FileSystemEventArgs參數(shù)中提供了相關(guān)文件的信息:如文件名,路徑,改變類型,Renamed事件中還可以了解到改變前的文件名和路徑。如果要禁用這些事件,則將它的EnableRaisingEvents屬性設(shè)置為false。Created,Deleted,Renamed三個(gè)事件比較容易處理,但Changed事件就得當(dāng)心了,你需要設(shè)置它的NotifyFilter屬性以指示監(jiān)視那些類型的變化。否則,程序會在文件被修改時(shí)淹沒在不斷發(fā)生的事件中(緩存區(qū)溢出)。
????watcher.Path?=?appPath;
????watcher.Filter?=?"*.txt";
????watcher.IncludeSubdirectories?=?true;
????//?添加事件處理函數(shù)
????watcher.Created?+=?new?FileSystemEventHandler(OnChanged);
????watcher.Deleted?+=?new?FileSystemEventHandler(OnChanged);
????watcher.Changed?+=?new?FileSystemEventHandler(OnChanged);
????watcher.Renamed?+=?new?RenamedEventHandler(OnRenamed);
????{
????????string?renamedFormat?=?"File:?{0}?被重命名為?:{1}";
????????txtChangedInfo.Text?=?string.Format(renamedFormat,?e.OldFullPath,?e.FullPath);
????}
????void?OnChanged(object?sender,?FileSystemEventArgs?e)
????{
????????//?顯示通知信息
????????txtChangedInfo.Text?=?"文件:?"?+?e.FullPath?+?"發(fā)生改變"?+?Environment.NewLine;
????????txtChangedInfo.Text?+=?"改變類型:?"?+?e.ChangeType.ToString();
????}
??? 問題7:如何使用獨(dú)立存儲文件;
??? 解決方案:
??? 有時(shí)你需要將數(shù)據(jù)存儲在文件中,但對本地硬盤驅(qū)動器卻沒有必要的權(quán)限(FileIOPermission)。這時(shí)要用到System.IO.IsolatedStorage命名空間中的類,這些類允許你的程序在特定用戶的目錄下將數(shù)據(jù)寫入文件而不需要直接訪問硬盤驅(qū)動器的權(quán)限:
?
????//?創(chuàng)建當(dāng)前用戶的獨(dú)立存儲????using?(IsolatedStorageFile?store?=?IsolatedStorageFile.GetUserStoreForAssembly())
????{
????????//?創(chuàng)建一個(gè)文件夾
????????store.CreateDirectory("MyFolder");
????????//?創(chuàng)建一個(gè)獨(dú)立存儲文件
????????using?(Stream?fs?=?new?IsolatedStorageFileStream("MyFile.txt",?FileMode.Create,?store))
????????{
????????????StreamWriter?writer?=?new?StreamWriter(fs);
????????????writer.WriteLine("Test?Line!");
????????????writer.Flush();
????????}
????????Debug.WriteLine("當(dāng)前大小:"?+?store.CurrentSize.ToString()?+?Environment.NewLine);
????????Debug.WriteLine("范圍:"?+?store.Scope.ToString()?+?Environment.NewLine);
????????string[]?files?=?store.GetFileNames("*.*");
????????if?(files.Length?>?0)
????????{
????????????Debug.WriteLine("當(dāng)前文件:"?+?Environment.NewLine);
????????????foreach?(string?file?in?files)
????????????{
????????????????Debug.WriteLine(file?+?Environment.NewLine);
????????????}
????????}
??? }
??? 注意:本文部分內(nèi)容為作示例都作了簡化,所以肯定會有不合理之處,僅希望能為您提供一些線索和思路。在使用前還請多多參考相關(guān)資料。
| ? |
總結(jié)
以上是生活随笔為你收集整理的路径,文件,目录,I/O常见操作汇总的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 干洗店多少钱啊?
- 下一篇: 请问一下各位,这是哪部电影里的呀?谢谢啦