0. 前言 這是對C 基礎系列的一個總結,現在我們利用之前學到的知識做一個小小的工具來給我們使用。 如果有看過IO篇的小伙伴,應該有印象。當時我提過一個場景描述,我們在平時使用系統的時候,經常會為了找某個文件的位置而煩惱。那麼我們現在嘗試寫一個控制台程式來幫助我們找文件的具體位置。 1. 分析 好, ...
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. 總結
代碼進行到這裡了,可以說基本功能已經完成。如果有小伙伴嘗試使用示例代碼的話,可能會遇到各種問題,下一篇繼續為大家在現有知識基礎上做優化,讓它成為一個真正意義上可以使用的小工具。
更多內容煩請關註我的博客《高先生小屋》