0. 前言
這是對C# 基礎系列的一個總結,現在我們利用之前學到的知識做一個小小的工具來給我們使用,
如果有看過IO篇的小伙伴,應該有印象,當時我提過一個場景描述,我們在平時使用系統的時候,經常會為了找某個檔案的位置而煩惱,那么我們現在嘗試寫一個控制臺程式來幫助我們找檔案的具體位置,
1. 分析
好,大家應該初步了解了需求內容,然后讓我們來做一個簡單的需求分析:
- 簡單分析一下需求包括哪些功能點
- 規劃各個功能點的實作方式
嗯,理論上講還有一大堆的步驟,但因為是個練手的小專案就不扯那么多沒用的了,簡單來講就是,分兩步:
- 抓取系統可以訪問的所有檔案,并保存其全路徑
- 根據輸入的引數查詢檔案的全路徑
需求分析完了,然后尋找可以實作的技術,我們現有的技術有IO、檔案/路徑操作、任務模式等技術,那么可以供我們選擇的技術一目了然了:通過檔案/目錄/路徑API訪問所有的檔案目錄,使用字典保存,然后使用Linq查詢檔案所在目錄,
OK,需求分析完了,技術也確認了,那么我們現在開始吧,小伙伴們跟緊了哦,車速不快的,
2. 開始
這里簡單演示一下如何用Rider和VSCode、Visual Studio2019創建專案,
2.1. 創建一個名為 FileFinder的專案
a.使用Rider:
點擊箭頭所指方向:

先在左側選擇Console Application,然后修改 Project name,最后修改 Solution Directory為自己的目錄:

然后點擊 Create,創建完成結果如下:

Rider創建專案的步驟在Windows、Linux、Mac三個系統都是一樣的,
b. 使用VS Code創建專案
使用VS Code創建專案與Rider和Visual Studio有所不同,步驟比較繁瑣:
先在合適的檔案夾下創建一個fileFinder目錄,并在fileFinder目錄下打開命令列,輸入以下命令:
dotnet new sln -n fileFinder # 創建一個名為 fileFinder 的解決方案
dotnet new console -n fileFinder # 創建一個名為 fileFinder的控制臺程式
dotnet sln add fileFinder # 把 fileFinder的專案添加到fileFinder的解決方案里
最終結果應該是這樣的:

c.使用 Visual Studio

選擇【創建新專案】

注意框住地方的選擇,選控制臺程式,然后點擊下一步

填寫專案名稱、路徑,點擊創建

2.2 開始撰寫程式
現在我們創建完成了一個專案,然后可以開始撰寫我們的程式了,
首先創建一個遍歷所有目錄的方法:
public static Dictionary<string,List<string>> OverDirectories()
{
//
return null;
}
現在我們有一個問題,因為Windows的特殊性,目錄結構分為了磁盤:\檔案夾這種形式,我們沒法通過設定一個根目錄去遍歷,這時候就要借助一下官方檔案了,通過查閱API,我們發現一個類:
public sealed class DriveInfo : System.Runtime.Serialization.ISerializable//提供對有關驅動器的資訊的訪問,
有一個方法:
public static System.IO.DriveInfo[] GetDrives ();// 檢索計算機上的所有邏輯驅動器的驅動器名稱,
再看一下屬性:
public string Name { get; }// 獲取驅動器的名稱,如 C:\,
public System.IO.DirectoryInfo RootDirectory { get; }// 獲取驅動器的根目錄,
初步查看滿足我們的需要,先在Program.cs的頭添加命名空間參考:
using System.IO;
表示在這個代碼檔案中會使用這個命名空間的類或者結構體等元素,
在專案中撰寫一個方法:
public static void GetDrivers()
{
var drives = DriveInfo.GetDrives();
foreach(var drive in drives)
{
Console.WriteLine($"驅動器名稱:{drive.Name}:\t {drive.RootDirectory}");
}
}
然后修改Main方法為:
static void Main(string[] args)
{
GetDrivers();
}
運行程式,下圖是Linux系統的列印結果:(Rider/Visual Studio的運行程式快捷鍵是F5)

經過完美符合我們的需求,修改GetDrivers方法,使其可以回傳所有驅動器的根目錄:
先引入以下命名空間的參考:
using System.Linq;// Linq的支持
using System.Collections.Generic;//泛型集合的支持
修改方法如下:
public static List<DirectoryInfo> GetDrivers()
{
var drives = DriveInfo.GetDrives();
return drives.Select(p=>p.RootDirectory).ToList();
}
然后回到方法OverDirectories里,先獲取所有的驅動器,遍歷所有驅動器下的所有目錄和檔案,之后對遍歷結果歸類:
修改OverrDirectories方法:
public static Dictionary<string,List<string>> OverDirectories(DirectoryInfo rootDirectory)
{
var dict = new Dictionary<string, List<string>>();// 創建一個保存資料的 字典型別
foreach(var file in rootDirectory.EnumerateFiles()) //列舉當前目錄下的所有檔案
{
var key = Path.GetFileNameWithoutExtension(file.Name); //獲取無擴展名的檔案名
if(!dict.ContainsKey(key)) //檢查dict是否存放過 檔案名,如果沒有,則創建一個串列,如果有則在串列中添加一條檔案的全路徑
{
dict[key] = new List<string>();
}
dict[key].Add(file.FullName);
}
// 列舉當前目錄的子目錄,遞回呼叫該方法
var dirs = rootDirectory.EnumerateDirectories().Select(OverDirectories);
foreach(var dir in dirs)//處理回傳的字典列舉,將資料合并到當前dict變數中
{
foreach(var key in dir.Keys)
{
if(!dict.ContainsKey(key))
{
dict[key] = new List<string>();
}
dict[key].AddRange(dir[key]);
}
}
// 回傳結果
return dict;
}
我們簡單測驗一下,修改Main方法:
static void Main(string[] args)
{
var drivers = GetDrivers();
var results = OverDirectories(drivers[0]);
Console.WriteLine(results);
}
嗯,如果不出意外的話,你應該能得到類似如下的提示:

這是因為在系統中(不管哪種系統)會有一些檔案或者目錄是我們沒有權限訪問的,這時候就必須用try/catch處理這些沒有訪問權限的目錄和檔案,因為我們平時使用不會 把檔案放到這些目錄下面,所以我們可以簡單的略過這些目錄,
同時觀察一下,GetDrivers 回傳的是一組DirectoryInfo實體,而OverDirectories每次處理一個目錄,然后回傳一個字典集合,所以我們需要整合這些集合,但我們在OverDirectories里撰寫過相似的代碼,為了減少重復代碼的撰寫,提取這部分代碼為一個方法:
public static Dictionary<string,List<string>> Concat(params Dictionary<string,List<string>>[] dicts)
{
var dict = new Dictionary<string,List<string>>();
foreach(var dir in dicts)
{
foreach(var key in dir.Keys)
{
if(!dict.ContainsKey(key))
{
dict[key] = new List<string>();
}
dict[key].AddRange(dir[key]);
}
}
return dict;
}
params 是C#可變引數串列關鍵字,宣告方式: params T[] values,表示方法可以接收任意個T型別的引數,方法中接到的是一個陣列
繼續改造 OverDirectories方法,增加例外處理:
public static Dictionary<string,List<string>> OverDirectories(DirectoryInfo rootDirectory)
{
var dict = new Dictionary<string, List<string>>();
IEnumerable<FileInfo> files = new List<FileInfo>();
try
{
files = rootDirectory.EnumerateFiles();
}
catch(Exception e)
{
Console.WriteLine($"錯誤資訊:{e}");//列印錯誤資訊
}
foreach(var file in files)
{
var key = Path.GetFileNameWithoutExtension(file.Name);
if(!dict.ContainsKey(key))
{
dict[key] = new List<string>();
}
dict[key].Add(file.FullName);
}
try
{
var dicts = rootDirectory.EnumerateDirectories().Select(OverDirectories);
return Concat(dicts.Append(dict).ToArray());
}
catch (System.Exception e)
{
Console.WriteLine($"錯誤資訊:{e}");//列印錯誤資訊
}
return dict;
}
最后修改 Main方法,使其支持使用用戶輸入的字串進行查詢:
static void Main(string[] args)
{
var drivers = GetDrivers();
var results = Concat(drivers.Select(OverDirectories).ToArray());
Console.WriteLine("請輸入要查詢的檔案名:");
var search = Console.ReadLine().Trim();
var keys = results.Keys.Where(p=>p.Contains(search));
foreach(var key in keys)
{
var list = results[key];
Console.WriteLine("查找到路徑是:");
foreach(var path in list)
{
Console.WriteLine(path);
}
}
}
3. 總結
代碼進行到這里了,可以說基本功能已經完成,如果有小伙伴嘗試使用示例代碼的話,可能會遇到各種問題,下一篇繼續為大家在現有知識基礎上做優化,讓它成為一個真正意義上可以使用的小工具,
更多內容煩請關注我的博客《高先生小屋》

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/42836.html
標籤:C#
上一篇:C# get class and method summary
下一篇:ref和out的使用與區別
