C# 基礎知識系列- 17 小工具優化
- 前言
不知道有沒有動手能力強的小夥伴照著上一篇的內容寫過程式呢?如果有的話,應該會在使用的時候發現以下幾個問題:
每次啟動都需要經過漫長的時間去遍歷磁碟裡的檔案目錄
因為資料是用的字典儲存的,所以會消耗大量的記憶體空間
不能多次查詢
現在我們就針對這些問題,讓我們的小工具實用起來。
- 分析與實現
在動手之前,我們先分析一下問題。在實際開發之前,無論是接到什麼需求都要先仔細分析一下,確定好方案再動手方為開發的正道。嗯,沒毛病。因為開發過程中跟產品對線、跟客戶對線要佔整個專案的一半左右時間。好了,不廢話了。繼續:
遍歷檔案目錄的時間過長,那麼我們是不是可以用非同步併發去遍歷呢?
資料用字典儲存會消耗記憶體空間,那麼我們是不是可以用其他的方式儲存呢?
不能多次查詢,是不是可以使用迴圈,然後設定一個退出條件?
1.1 C#的非同步/併發實現
在C#裡,非同步和併發的實現是依據執行緒、任務來實現的。在之前《C# 基礎知識系列- 12 任務和多執行緒》裡大概介紹了一下執行緒和任務,我們知道執行緒本身是沒法返回資料的,它與主執行緒進行資料互動的過程十分需要注意執行緒安全。而任務可以返回資料,不需要像執行緒一樣小心翼翼地與主執行緒進行資料互動。任務有一個優點,它比執行緒更輕量,所以在當前環境下我們可以試試任務。
當然,執行緒也有優點,那就是vb.net教程執行緒的執行環境相對更封閉一點,它能完成一個長的大型運算。
那麼繼續上一篇的內容,先引用 :
using System.Threading.Tasks;
先提取一組根據可列舉目錄集合建立任務組並取c#教程得結果的方法:
public static Dictionary<string,List<string>> OverDirectories(IEnumerable<DirectoryInfo> directories)
{
var tasks = directories.Select(dir => Task.Run(()=>OverDirectories(dir))).ToArray();
Task.WaitAll(tasks);// 這行的意思是等待所有任務完成
return Concat(tasks.Select(t=>t.Result).ToArray());
}
然後改造原有的OverDirectories方法:
public static Dictionary<string,List<string>> OverDirectories(DirectoryInfo rootDirectory)
{
Console.WriteLine($"正在遍歷目錄:{rootDirectory.FullName}");
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 dirs = rootDirectory.EnumerateDirectories();
return Concat(dict, OverDirectories(dirs));// 採用執行緒版的方法進行遍歷
}
catch (System.Exception e)
{
Console.WriteLine($"錯誤資訊:{e}");//列印錯誤資訊
}
return dict;
}
1.2 資料複用
理想狀態下,我們的資料應該是儲存在資料庫的,但因為資料庫的操作是在下一系列的教程中,所以目前只能捨棄這個設想。
那麼,利用現有方式,我們可以使用檔案作為快取的方式,也就是說把資料儲存在檔案裡,在需要的時候從檔案中讀取出來。這時候就需要一組操作檔案的方法。
首先,宣告一個靜態變數:
public static readonly string TempFile = “temp.txt”;
然後編寫讀取、存放資料的方法:
public static void WriteLinesToTemp(List<string> lines)
{
File.AppendAllLines(TempFile, lines);
}
public static List<string> Search(string file)
{
var lines = File.ReadLines(file);
var results = lines.Where(line=>Path.GetFileNameWithoutExtension(line).Contains(file));
return results.ToList();
}
這時候在檔案中存放的都是路徑檔案,所以需要重新修改遍歷檔案路徑的方法,只保留路徑:
public static List<string> OverDirectories(DirectoryInfo rootDirectory)
{
Console.WriteLine($"正在遍歷目錄:{rootDirectory.FullName}");
List<string> files = new List<string>();
try
{
files.AddRange(rootDirectory.GetFiles().Select(f=>f.FullName).ToList());
Console.WriteLine($"在目錄:{rootDirectory.FullName} 下 找到 檔案:{files.Count} 個");
}
catch(Exception e)
{
Console.WriteLine($"載入目錄:{rootDirectory.FullName} 中\t錯誤資訊:{e}");//列印錯誤資訊
}
try
{
var dirs = rootDirectory.GetDirectories();
OverDirectories(dirs);
}
catch (System.Exception e)
{
Console.WriteLine($"在下探目錄{rootDirectory.FullName}時發生錯誤:{e}");
}
return files;
}
public static void OverDirectories(IEnumerable<DirectoryInfo> directories)
{
var tasks =new List<Task<List<string>>>( directories.Select(dir => Task.Run(()=>OverDirectories(dir))));
while(tasks.Any())
{
var completeds = tasks.Where(t=>t.IsCompleted).ToList(); // 提取所有已完成的任務
foreach(var t in completeds)
{
WriteLinesToTemp(t.Result);// 儲存檔案列表
tasks.Remove(t);//移除已處理的任務
}
}
}
最後修改主方法,設定啟動時遍歷路徑的規則:
static void Main(string[] args)
{
if(!File.Exists(TempFile))// 快取檔案存在,則認為上次已經遍歷成功了
{
var drivers = GetDrivers();
OverDirectories(drivers);
}
Console.WriteLine("請輸入要查詢的檔名:");
var search = Console.ReadLine().Trim();
}
1.3 迴圈使用並設定退出條件
設定使用者輸入q或Q的時候退出程式,這時候就需要改造Main方法了:
static void Main(string[] args)
{
Console.WriteLine("檔案查詢小工具啟動了……");
if(!File.Exists(TempFile))
{
Console.WriteLine("尚未載入快取記錄,資料載入中……");
var drivers = GetDrivers();
OverDirectories(drivers);
Console.WriteLine("資料載入完成");
Thread.Sleep(500);
Console.Clear();// 清除控制檯
}
while(true)
{
Console.WriteLine("請輸入要查詢的檔名(輸入q/Q 退出):");
var search = Console.ReadLine().Trim();// 去除多餘的空白字元
if(search == "q" || search == "Q")//新增退出條件
{
break;
}
Console.WriteLine("查詢中……");
var results = Search(search);
Console.WriteLine("查詢結果:");
foreach(var r in results)
{
Console.WriteLine(r);
}
}
Console.WriteLine("程式已退出!");
}
在main 方法里加了很多提示語句,以方便使用。
相關文章
- Java基礎知識系列—序列化Java
- C# 基礎知識系列-13 常見類庫(三)C#
- C# 基礎知識系列- 14 IO篇 檔案的操作C#
- C# 基礎知識系列- 9 字串的更多用法(二)C#字串
- 【Git 系列】基礎知識全集Git
- MySQL資料庫基礎知識及優化MySql資料庫優化
- Redis基礎知識(學習筆記17--持久化 (3))Redis筆記持久化
- C# 基礎知識系列- 13 常見類庫介紹(一)C#
- java基礎知識-序列化/反序列化-gson基礎知識Java
- gRPC-Protocol基礎知識-C#篇RPCProtocolC#
- IdentityServer4系列 | 初識基礎知識點IDEServer
- UI自動化基礎知識UI
- 小白系列:資料庫基礎知識解析資料庫
- WebSocket系列之基礎知識入門篇Web
- [01-jwt]C# JWT基礎知識詳解JWTC#
- 基礎知識
- C# 基礎知識系列- 13 常見類庫介紹(二)日期時間類C#
- OpenStack關鍵技術系列: Libvirt基礎知識
- Python基礎知識之Django框架優缺點!PythonDjango框架
- Envoy基礎知識
- DockerFile基礎知識Docker
- Webpack 基礎知識Web
- js基礎知識JS
- React基礎知識React
- 程式基礎知識
- Docker基礎知識Docker
- qml基礎知識
- Mybatis基礎知識MyBatis
- python基礎知識Python
- Hadoop基礎知識Hadoop
- webpack基礎知識Web
- AI 基礎知識AI
- JSP基礎知識JS
- Dart基礎知識Dart
- RabbitMQ基礎知識MQ
- Android基礎知識Android
- 1、基礎知識
- 前端基礎知識前端