zoukankan      html  css  js  c++  java
  • C# 基础知识系列- 17 实战篇 编写一个小工具(1)

    0. 前言

    这是对C# 基础系列的一个总结,现在我们利用之前学到的知识做一个小小的工具来给我们使用。

    如果有看过IO篇的小伙伴,应该有印象。当时我提过一个场景描述,我们在平时使用系统的时候,经常会为了找某个文件的位置而烦恼。那么我们现在尝试写一个控制台程序来帮助我们找文件的具体位置。

    1. 分析

    好,大家应该初步了解了需求内容。然后让我们来做一个简单的需求分析:

    1. 简单分析一下需求包括哪些功能点
    2. 规划各个功能点的实现方式

    嗯,理论上讲还有一大堆的步骤,但因为是个练手的小项目就不扯那么多没用的了。简单来讲就是,分两步:

    1. 抓取系统可以访问的所有文件,并保存其全路径
    2. 根据输入的参数查询文件的全路径

    需求分析完了,然后寻找可以实现的技术,我们现有的技术有IO、文件/路径操作、任务模式等技术,那么可以供我们选择的技术一目了然了:通过文件/目录/路径API访问所有的文件目录,使用字典保存,然后使用Linq查询文件所在目录。

    OK,需求分析完了,技术也确认了。那么我们现在开始吧,小伙伴们跟紧了哦,车速不快的。

    2. 开始

    这里简单演示一下如何用Rider和VSCode、Visual Studio2019创建项目。

    2.1. 创建一个名为 FileFinder的项目

    a.使用Rider:

    点击箭头所指方向:

    image-20200506213821025

    先在左侧选择Console Application,然后修改 Project name,最后修改 Solution Directory为自己的目录:

    image-20200506214124769

    然后点击 Create,创建完成结果如下:

    image-20200506214423649

    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的解决方案里
    

    最终结果应该是这样的:

    image-20200506220949536

    c.使用 Visual Studio

    file

    选择【创建新项目】
    file
    注意框住地方的选择,选控制台程序,然后点击下一步
    file
    填写项目名称、路径,点击创建
    file

    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}:	 {drive.RootDirectory}");
        }
    }
    

    然后修改Main方法为:

    static void Main(string[] args)
    {
        GetDrivers();
    }
    

    运行程序,下图是Linux系统的打印结果:(Rider/Visual Studio的运行程序快捷键是F5)

    image-20200506224209939

    经过完美符合我们的需求,修改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);
    }
    

    嗯,如果不出意外的话,你应该能得到类似如下的提示:

    image-20200506234510641

    这是因为在系统中(不管哪种系统)会有一些文件或者目录是我们没有权限访问的,这时候就必须用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. 总结

    代码进行到这里了,可以说基本功能已经完成。如果有小伙伴尝试使用示例代码的话,可能会遇到各种问题,下一篇继续为大家在现有知识基础上做优化,让它成为一个真正意义上可以使用的小工具。

    更多内容烦请关注我的博客《高先生小屋》

    file

  • 相关阅读:
    HDU 2196 Computer
    HDU 1520 Anniversary party
    POJ 1217 FOUR QUARTERS
    POJ 2184 Cow Exhibition
    HDU 2639 Bone Collector II
    POJ 3181 Dollar Dayz
    POJ 1787 Charlie's Change
    POJ 2063 Investment
    HDU 1114 Piggy-Bank
    Lca hdu 2874 Connections between cities
  • 原文地址:https://www.cnblogs.com/c7jie/p/12842459.html
Copyright © 2011-2022 走看看