標籤:des blog http 使用 os io 檔案 資料
檔案操作是程式中非常基礎和重要的內容,而路徑、檔案、目錄以及I/O都是在進行檔案操作時的常見主題,這裡想把這些常見的問題作個總結,對於每個問題,盡量提供一些解決方案,即使沒有你想要的答案,也希望能提供給你一點有益的思路,如果你有好的建議,懇請能夠留言,使這些內容更加完善。
主要內容:
一、路徑的相關操作, 如判斷路徑是否合法,路徑類型,路徑的特定部分,合并路徑,系統檔案夾路徑等內容;
二、相關通用檔案對話方塊,這些對話方塊可以協助我們操作檔案系統中的檔案和目錄;
三、檔案、目錄、磁碟機的操作,如擷取它們的基本資料,擷取和設定檔案和目錄的屬性,檔案的版本資訊,
搜尋檔案和目錄,檔案判等,複製、移動、刪除、重新命名檔案和目錄;
四、讀寫檔案,包括臨時檔案,隨機檔案名稱等;
五、對檔案系統的監視;
這一篇就先寫一下前兩部分。
一、路徑相關操作
問題1:如何判定一個給定的路徑是否有效/合法;
解決方案:通過Path.GetInvalidPathChars或Path.GetInvalidFileNameChars方法獲得非法的路徑/檔案名稱字元,可以
根據它來判斷路徑中是否包含非法字元;
問題2:如何確定一個路徑字串是表示目錄還是檔案;
解決方案:
1、使用Directory.Exists或File.Exist方法,如果前者為真,則路徑表示目錄;如果後者為真,則路徑表示檔案;
2、上面的方法有個缺點就是不能處理那些不存在的檔案或目錄。這時可以考慮使用Path.GetFileName方法獲得
其包含的檔案名稱,如果一個路徑不為空白,而檔案名稱為空白那麼它表示目錄,否則表示檔案;
問題3:如何獲得路徑的某個特定部分(如檔案名稱、副檔名等);
解決方案:
下面是幾個相關方法:
Path.GetDirectoryName :返回指定路徑字串的目錄資訊;
Path.GetExtension : 返回指定的路徑字串的副檔名;
Path.GetFileName : 返回指定路徑字串的檔案名稱和副檔名;
Path.GetFileNameWithoutExtension :返回不具有副檔名的路徑字串的檔案名稱;
Path.GetPathRoot :擷取指定路徑的根目錄資訊;
(更多內容還請參考MSDN)
問題4:如何準確地合并兩個路徑而不用去擔心那個煩人的”\”字元;
解決方案:
使用Path.Combine方法,它會幫你處理煩人的”\”;
問題5:如何獲得系統目錄的的路徑(如案頭,我的文件,臨時檔案夾等);
解決方案:
主要是使用System. Environment類的相關屬性和方法:
Environment. SystemDirectory屬性:擷取系統目錄的完全限定路徑;
Environment. GetFolderPath方法:該方法接受的參數類型為Environment.SpecialFolder枚舉,
通過這個方法可以獲得大量系統檔案夾的路徑,如我的電腦,我的電腦,案頭,系統目錄等;
(更多內容還請參考MSDN);
Path.GetTempPath方法:返回當前系統的臨時檔案夾的路徑;
問題6:如何判斷一個路徑是絕對路徑還是相對路徑;
解決方案:
使用Path.IsPathRooted方法;
問題7:如何讀取或設定目前的目錄;
解決方案:
使用Directory類的GetCurrentDirectory和SetCurrentDirectory方法;
問題8:如何使用相對路徑;
解決方案:
設定目前的目錄後(見問題7),就可以使用相對路徑了。對於一個相對路徑,我們可以
使用Path.GetFullPath方法獲得它的完全限定路徑(絕對路徑)。
注意:如果打算使用相對路徑,建議你將工作目錄設定為各個互動檔案的共同起點,否則可能會引入
一些不易發現的安全隱患,被惡意使用者利用來訪問系統檔案。
更多內容:
通常我們可以使用System.IO.Path類來處理路徑。該類提供了一套方法和屬性用於對包含檔案或目錄路徑資訊的字串執行操作,這些操作是以跨平台的方式執行的,而這些方法和屬性都是靜態。
注意路徑僅僅是提供檔案或目錄位置的字串。路徑不必指向磁碟上的位置,例如,路徑可以映射到記憶體中或裝置上的位置。路徑的準確格式是由當前平台確定的。例如,在某些系統上,路徑可以磁碟機代號或卷號開始,而此元素在其他系統中是不存在的。在某些系統上,檔案路徑可以包含副檔名,副檔名指示在檔案中儲存的資訊的類型。副檔名的格式是與平台相關的;例如,某些系統將副檔名的長度限制為 3 個字元,而其他系統則沒有這樣的限制。當前平台還確定用於分隔路徑中各元素的字元集,以及確定在指定路徑時不能使用的字元集。因為這些差異,所以 Path 類的欄位以及 Path 類的某些成員的準確行為是與平台相關的。
路徑可以包含絕對或相對位置資訊。絕對路徑完整指定一個位置:檔案或目錄可被唯一標識,而與當前位置無關。相對路徑指定部分位置:當定位用相對路徑指定的檔案時,當前位置用作起始點。
Path類的大多數成員不與檔案系統互動,並且不驗證路徑字串指定的檔案是否存在。修改路徑字串的Path 類成員(例如 ChangeExtension)對檔案系統中檔案的名稱沒有影響。但Path成員確實驗證指定路徑字串的內容;並且如果字串包含在路徑字串中無效的字元(如 InvalidPathChars 中的定義),則引發 ArgumentException異常。例如,在基於 Windows 的案頭平台上,無效路徑字元可能包括引號 (")、小於符號 (<)、大於符號 (>)、管道符號 (|)、退格 (\b)、空 (\0) 以及從 16 到 18 和從 20 到 25的 Unicode 字元。
Path 類的成員使您可以快速方便地執行常見操作,例如確定副檔名是否是路徑的一部分,以及將兩個字串組合成一個路徑名。
多數情況下,如果這些方法接收了無效的路徑會拋出異常,但如果路徑名是因為包含了萬用字元(*或?)從而無效,則不會拋出異常(可以使用GetInvalidPathChars方法來非法的路徑字元)。我們可以根據該原則判斷一個路徑是否合法。
二、相關的通用檔案對話方塊
1、檔案夾瀏覽對話方塊(FolderBrowserDialog類)
使用者可以通過該對話方塊瀏覽、建立並選擇檔案夾
主要屬性:
Description:樹狀檢視控制項上顯示的說明文本,如中的”選擇要進行計算的目錄”;
RootFolder:擷取或設定從其開始瀏覽的根資料夾,如中設定的我的電腦(預設為案頭);
SelectedPath:擷取或設定使用者選定的路徑,如果設定了該屬性,開啟對話方塊時會定位到指定路徑,預設為根資料夾,關閉對話方塊時根據該屬性擷取使用者使用者選定的路徑;
ShowNewFolderButton:擷取或設定是否顯示建立對話方塊按鈕;
主要方法:
ShowDialog:開啟該對話方塊,傳回值為DialogResult類型值,如果為DialogResult.OK,則可以由SelectedPath屬性擷取使用者選定的路徑;
dlgOpenFolder.Description = "選擇要進行計算的目錄";
dlgOpenFolder.RootFolder = Environment.SpecialFolder.MyComputer;
dlgOpenFolder.ShowNewFolderButton = true;
DialogResult result = dlgOpenFolder.ShowDialog(this);
if (result == DialogResult.OK)
{
txtDirPath.Text = dlgOpenFolder.SelectedPath;
}
2、開啟檔案對話方塊(OpenFileDialog類)
使用者可以通過該對話方塊選擇一個檔案
主要屬性:
CheckFileExists:該值指示如果使用者指定不存在的檔案名稱,對話方塊是否顯示警告;
FileName(s):擷取或設定一個包含在檔案對話方塊中選定的檔案名稱的字串;
Filter:擷取或設定對話方塊的檔案類型列表;
FilterIndex:對話方塊的檔案類型列表的索引(基於1的);
InitialDirectory:擷取或設定檔案對話方塊顯示的初始目錄;
Multiselect:該值指示對話方塊是否允許選擇多個檔案;
ShowReadOnly:該值指示對話方塊是否包含唯讀複選框;
Title:擷取或設定檔案對話方塊標題;
主要方法:
OpenFile:開啟使用者選定的具有唯讀許可權的檔案;
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類)
使用者可以通過該對話方塊儲存一個檔案
主要屬性:
大部分與開啟檔案對話方塊類似,此處略過,下面幾個值得注意:
CreatePrompt:該值指示如果使用者指定不存在的檔案,是否提示使用者允許建立該檔案;
OverwritePrompt:該值指示如果使用者指定的檔案名稱已存在,對話方塊是否顯示警告;
主要方法:
OpenFile:開啟使用者選定的具有讀/寫入權限的檔案;
ShowDialog:開啟該強制回應對話方塊;
範例程式碼:
dlgSaveFile.Title = "開啟目標檔案";
dlgSaveFile.InitialDirectory = @"C:\Inetpub\";
dlgSaveFile.Filter = "文字檔 (*.txt)|*.txt|所有檔案 (*.*)|*.*";
dlgSaveFile.FilterIndex = 2;
DialogResult dr = dlgSaveFile.ShowDialog();
if (dr == DialogResult.OK)
{
string fileName = dlgSaveFile.FileName;
}
至此,我們操作的都只是路徑,要知道,這些路徑僅僅是字串,還沒有涉及到檔案系統中的真實檔案。
三、檔案和目錄相關操作
檔案和目錄操作涉及的類主要是:FileInfo,DirectoryInfo,DriveInfo,可以認為它們的一個執行個體對應著一個檔案、目錄、磁碟機。它們的用法類似,一般是將檔案、目錄或磁碟機的路徑作為參數傳遞給相應的建構函式建立一個執行個體,然後訪問它們的屬性和方法。
注意下面幾點:
FileInfo 類和 DirectoryInfo 類都繼承自抽象類別 FileSystemInfo , FileSystemInfo 類定義了一些通用的屬性,如 CreationTime 、 Exists 等。但 DriveInfo 類沒有繼承 FileSystemInfo 類,所以它也就沒有上面提到的那些通用屬性了。
FileInfo 類和 DirectoryInfo 類的對象公開的屬性值都是第一次查詢時擷取的值,如果在以此查詢之後檔案或目錄發生了改動,就必須調用它們的 Refresh 方法來更新這些屬性。但 DriveInfo 則無需這麼做,它的屬性每次都會讀取檔案系統最新的資訊。
在建立檔案、目錄或磁碟機的執行個體時,如果使用了一個不存在的路徑,並不會報錯,這是你得到一個對象,該對象表示一個並不存在的實體,這意味著它的 Exists 屬性(對於 DriveInfo 來說是 IsReady 屬性)值為 false 。你仍然可以操作該實體,但如果嘗試其它的大多數屬性,就會引發相應的 FileNotFoundException 、 DirectoryNotFoundException 或 DriveNotFoundException 異常。
另外,還可以使用 File / Directory 類,這兩個類的成員都是靜態方法,所以如果只想執行一個操作,那麼使用 File/Directory 中的靜態方法的效率比使用相應的 FileInfo / DirectoryInfo中的 執行個體方法可能更高。所有的 File / Directory 方法都要求當前所操作的檔案 / 目錄的路徑。 注意: File / Directory 類的靜態方法對所有方法都執行安全檢查。如果打算多次重用某個對象,可考慮改用 FileInfo / DirectoryInfo 的相應執行個體方法,因為並不總是需要安全檢查。
下面是一些常見的問題:
問題1:如何擷取指定檔案的基本資料;
解決方案:可以使用FileInfo類的相關屬性:
FileInfo.Exists:擷取指定檔案是否存在;
FileInfo.Name,FileInfo.Extensioin:擷取檔案的名稱和副檔名;
FileInfo.FullName:擷取檔案的全限定名稱(完整路徑);
FileInfo.Directory:擷取檔案所在目錄,傳回型別為DirectoryInfo;
FileInfo.DirectoryName:擷取檔案所在目錄的路徑(完整路徑);
FileInfo.Length:擷取檔案的大小(位元組數);
FileInfo.IsReadOnly:擷取檔案是否唯讀;
FileInfo.Attributes:擷取或設定指定檔案的屬性,傳回型別為FileAttributes枚舉,可以是多個值的組合(見問題2);
FileInfo.CreationTime、FileInfo.LastAccessTime、FileInfo.LastWriteTime:分別用於擷取檔案的建立時間、訪問時間、修改時間;
(更多內容還請參考MSDN)
問題2:如何擷取和設定檔案的屬性,比如唯讀、存檔、隱藏等;
解決方案:
使用FileInfo.Attributes屬性可以擷取和設定檔案的屬性,該屬性類型為FileAttributes枚舉,該枚舉的每個值表示一種屬性,FileAttributes枚舉具有屬性(Attribute)FlagsAttribute,所以該枚舉的值可以進行組合,也就是一個檔案可以同時擁有多個屬性。下面看看具體的做法:
擷取屬性,比如判斷一個檔案是否是唯讀:
// 當檔案具有其它屬性時,這種做法會失敗
if (file.Attributes == FileAttributes.ReadOnly)
{
chkReadonly.Checked = true;
}
// 這種寫法就不會有問題了,它只檢查唯讀屬性
if ((file.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
chkReadonly.Checked = true;
}
設定屬性,比如添加和移除一個檔案的唯讀屬性:
if (chkReadonly.Checked)
{
// 添加唯讀屬性
file.Attributes |= FileAttributes.ReadOnly;
}
else
{
// 移除唯讀屬性
file.Attributes &= ~FileAttributes.ReadOnly;
}
問題3:如何擷取檔案的版本資訊(比如版本號碼,著作權聲明,公司名稱等);
解決方案:
使用FileVersionInfo類,該類有大量的版本資訊相關的屬性。通過它的靜態方法GetVersionInfo獲得該類的一個執行個體,然後就可以訪問指定檔案的版本資訊了,非常方便。如FileVersion表示檔案版本號碼,LegalCopyright表示指定檔案的著作權聲明,CompanyName表示指定檔案的公司名稱。(更多內容還請參考MSDN)
問題4:如何判斷兩個檔案的內容是否相同(精確匹配);
解決方案:
使用System.security.Cryptography.HashAlgorithm類為每個檔案產生一個雜湊碼,然後比較兩個雜湊碼是否一致。
在比較檔案內容的時候可以採用好幾種方法。例如,檢查檔案的某一特定部分是否一致;如果願意,你甚至可以逐位元組讀取檔案,逐位元組進行比較。這兩種方法都是可以的,但在某些情況下,還是使用雜湊碼演算法更為方便。
該演算法為一個檔案產生一個小的(通常約為20位元組)二進位”指紋”(binary fingerprint)。從統計學角度看,不同的檔案不可能產生相同的雜湊碼。事實上,即使是一個很小的改動(比如,修改了源檔案中的一個bit),也會有50%的幾率來改變雜湊碼中的每一個bit。因此,雜湊碼常常用於資料安全方面。
要產生一個雜湊碼,你必須首先建立一個HashAlgorithm對象,而這通常是調用HashAlgorithm.Create方法來完成的;然後調用HashAlgorithm.ComputeHash方法,它會返回一個儲存雜湊碼的位元組數組。代碼如下:
/// <summary>
/// 判斷兩個檔案內容是否一致
/// </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類的相關屬性和方法:
DirectoryInfo.Exists:擷取指定目錄是否存在;
DirectoryInfo.Name:擷取目錄的名稱;
DirectoryInfo.FullName:擷取目錄的全限定名稱(完整路徑);
DirectoryInfo.Attributes:擷取或設定指定目錄的屬性,傳回型別為FileAttributes枚舉,可以是多個值的組合;
DirectoryInfo.CreationTime、FileInfo.LastAccessTime、FileInfo.LastWriteTime:分別用於擷取目錄的建立時間、訪問時間、修改時間;
DirectoryInfo.Parent:擷取目錄的上級目錄,傳回型別為DirectoryInfo;
DirectoryInfo.Root:擷取目錄的根目錄,傳回型別為DirectoryInfo;
問題6:如何擷取指定目錄包含的檔案和子目錄;
解決方案:
DirectoryInfo.GetFiles():擷取目錄中(不包含子目錄)的檔案,傳回型別為FileInfo[],支援萬用字元尋找;
DirectoryInfo.GetDirectories():擷取目錄(不包含子目錄)的子目錄,
傳回型別為DirectoryInfo[],支援萬用字元尋找;
DirectoryInfo. GetFileSystemInfos():擷取指定目錄下(不包含子目錄)的檔案和子目錄,
傳回型別為FileSystemInfo[],支援萬用字元尋找;
問題7:如何獲得指定目錄的大小;
解決方案:
檢查目錄內的所有檔案,利用FileInfo.Length屬性擷取每個檔案的大小,然後進行合計,然後使用遞迴演算法處理所有的子目錄的檔案,參考下面代碼:
/// <summary>
/// 計算一個目錄的大小
/// </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參數為true
if (includeSubDir)
{
DirectoryInfo[] dirs = di.GetDirectories();
foreach (DirectoryInfo dir in dirs)
{
totalSize += CalculateDirSize(dir, includeSubDir);
}
}
return totalSize;
}
問題8:如何使用萬用字元搜尋指定目錄內的所有檔案;
解決方案:
使用DirectoryInfo.GetFiles方法的重載版本,它可以接受一個過濾運算式,返回FileInfo數組,另外它的參數還可以指定是否對子目錄進行尋找。如:
dir.GetFiles("*.txt", SearchOption.AllDirectories);
問題9:如何複製、移動、重新命名、刪除檔案和目錄;
解決方案:使用FileInfo和DirectoryInfo類。
下面是FileInfo類的相關方法:
FileInfo.CopyTo:將現有檔案複製到新檔案,其重載版本還允許覆蓋已存在檔案;
FileInfo.MoveTo:將指定檔案移到新位置,並提供指定新檔案名稱的選項,所以可以用來重新命名檔案(而不改變位置); FileInfo.Delete:永久刪除檔案,如果檔案不存在,則不執行任何操作;
FileInfo.Replace:使用當前FileInfo對象對應檔案的內容替換目標檔案,而且指定另一個檔案名稱作為被替換檔案的備份,微軟考慮實在周到。
下面是DirectoryInfo類的相關方法:
DirectoryInfo.Create:建立指定目錄,如果指定路徑中有多級目錄不存在,該方法會一一建立;
DirectoryInfo.CreateSubdirectory:建立當前對象對應的目錄的子目錄;
DirectoryInfo.MoveTo:將目錄(及其包含的內容)移動至一個新的目錄,也可用來重新命名目錄;
DirectoryInfo.Delete:刪除目錄(如果它存在的話)。如果要刪除一個包含子目錄的目錄,要使用它的重載版本,以指定遞迴刪除。
注意到了沒有?DirectoryInfo類少了一個CopyTo方法,不過我們可以通過遞迴來實現這個功能:
/// <summary>
/// 複製目錄到目標目錄
/// </summary>
/// <param name="source">來源目錄</param>
/// <param name="destination">目標目錄</param>
public static void CopyDirectory(DirectoryInfo source, DirectoryInfo destination)
{
// 如果兩個目錄相同,則無須複製
if (destination.FullName.Equals(source.FullName))
{
return;
}
// 如果目標目錄不存在,建立它
if (!destination.Exists)
{
destination.Create();
}
// 複製所有檔案
FileInfo[] files = source.GetFiles();
foreach (FileInfo file in files)
{
// 將檔案複製到目標目錄
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:如何獲得電腦的所有邏輯磁碟機;
解決方案:使用DriveInfo類(需要.NET 2.0)
DriveInfo.GetDrives():獲得電腦的所有邏輯磁碟機,傳回型別為DriveInfo[];
問題11:如何擷取指定磁碟機的資訊;
解決方案:
DriveInfo.Name:擷取磁碟機的名稱(如C:\);
DriveInfo.DriveType:擷取磁碟機的類型(如Fixed,CDRom,Removable,Network等);
DriveInfo.DriveFormat:擷取磁碟機的格式(如NTFS,FAT32,CDFS,UDF等);
DriveInfo.IsReady:擷取磁碟機是否已準備好,比如CD是否已放入CD磁碟機,如果磁碟機沒有準備好,訪問其資訊會引發IOException類型異常;
DriveInfo.AvailableFreeSpace:擷取磁碟機的可用空間;
DriveInfo.TotalFreeSpace:擷取磁碟機總的可用空間,它與AvailableFreeSpace的不同在於AvailableFreeSpace會磁碟配額的設定;
DriveInfo.TotalSize:擷取磁碟機總的空間;
DriveInfo.RootDirectory:獲得磁碟機的根目錄(DirectoryInfo類型);
至此,我們已經瞭解了檔案和目錄相關的一些基本操作。
轉自http://blog.163.com/wwxwb_913/blog/static/97685362010621112545464/?fromdm&fromSearch&isFromSearchEngine=yes