C ා basic knowledge series - 17 practical writing a small tool

0. Preface

This is a summary of the C ා Foundation Series. Now we use the knowledge we learned before to make a small tool for us to use.

If you have read the IO article, you should have an impression. At that time, I mentioned a scenario description. When we use the system, we often worry about finding the location of a file. Now let's try to write a console program to help us find the specific location of the file.

1. Analysis

Well, you should have a preliminary understanding of the requirements. Then let's do a simple needs analysis:

  1. Briefly analyze what function points are included in the requirements
  2. Plan the implementation of each function point

Well, there are a lot of steps in theory, but because it's a small project, it's useless. In short, there are two steps:

  1. Grab all files that the system can access and save the full path
  2. Query the full path of the file according to the input parameters

After analyzing the requirements, we can find the technologies that can be realized. Our existing technologies include IO, file / path operation, task mode and so on. Then the technologies we can choose are clear at a glance: access all the file directories through the file / directory / path API, use the dictionary to save, and then use Linq to query the directory where the files are located.

OK, the requirement analysis is finished, and the technology is confirmed. So let's start now. My friends are following me closely. The speed is not fast.

2. Start

Here is a simple demonstration of how to create a project with Rider, VSCode and visual studio 2019.

2.1. Create a project named FileFinder

a. Using Rider:

Click the direction indicated by the arrow:

First select Console Application on the left, then modify Project name, and finally modify Solution Directory as your own directory:

Then click Create to Create the result as follows:

The steps for Rider to create a project are the same for Windows, Linux and Mac.

b. Create project with VS Code

Using VS Code to create a project is different from Rider and Visual Studio, and the steps are tedious:

First, create a fileFinder directory in the appropriate folder, and open the command line in the fileFinder directory, and enter the following command:

dotnet new sln -n fileFinder # Create a solution called fileFinder
dotnet new console -n fileFinder # Create a console program called fileFinder
dotnet sln add fileFinder # Add the project of fileFinder to the solution of fileFinder

The end result should be this:

c. Using Visual Studio

Select create new project

Pay attention to the box selection, select the console program, and then click Next

Fill in the project name and path, and click Create

2.2 start programming

Now that we've created a project, we can start writing our program.

First, create a method to traverse all directories:

public static Dictionary<string,List<string>> OverDirectories()
{
    //
    return null;
}

Now we have a problem. Because of the particularity of Windows, the directory structure is divided into disks and folders. We can't traverse through setting a root directory. At this time, we need to use the official documents. By looking up the API, we find a class:

public sealed class DriveInfo : System.Runtime.Serialization.ISerializable//Provides access to information about the drive.

There is a way:

public static System.IO.DriveInfo[] GetDrives ();// Retrieves the drive names of all logical drives on the computer.

Let's look at the properties again:

public string Name { get; }// Gets the name of the drive, such as C: \.
public System.IO.DirectoryInfo RootDirectory { get; }// Gets the root of the drive.

To meet our needs, first add a namespace reference in the header of Program.cs:

using System.IO;

Indicates that elements such as classes or structures of this namespace will be used in this code file.

Write a method in the project:

public static void GetDrivers()
{
    var drives = DriveInfo.GetDrives();
    foreach(var drive in drives)
    {
        Console.WriteLine($"Drive name:{drive.Name}:\t {drive.RootDirectory}");
    }
}

Then modify the Main method to:

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

Run the program, and the following figure is the printing result of Linux system: (the shortcut key of the run program of Rider/Visual Studio is F5)

After meeting our requirements perfectly, modify the GetDrivers method so that it can return to the root directory of all drives:

Introduce a reference to the following namespace first:

using System.Linq;// Linq support
using System.Collections.Generic;//Support for generic collections

The modification method is as follows:

public static List<DirectoryInfo> GetDrivers()
{
    var drives = DriveInfo.GetDrives();
    return drives.Select(p=>p.RootDirectory).ToList();
}

Then go back to the method OverDirectories, first get all drives, traverse all directories and files under all drives, and then classify the traversal results:

Modify the OverrDirectories method:

public static Dictionary<string,List<string>> OverDirectories(DirectoryInfo rootDirectory)
{
    var dict = new Dictionary<string, List<string>>();// Create a dictionary type to hold data
    foreach(var file in rootDirectory.EnumerateFiles()) //Enumerate all files in the current directory
    {
        var key = Path.GetFileNameWithoutExtension(file.Name); //Get file name without extension
        if(!dict.ContainsKey(key)) //Check whether dict has saved the file name. If not, create a list. If so, add a full path of the file to the list
        {
            dict[key] = new List<string>();
        }
        dict[key].Add(file.FullName);
    }

    // Enumerate the subdirectories of the current directory, and call this method recursively
    var dirs = rootDirectory.EnumerateDirectories().Select(OverDirectories); 
    foreach(var dir in dirs)//Process the returned dictionary enumeration and merge the data into the current dict variable
    {
        foreach(var key in dir.Keys)
        {
            if(!dict.ContainsKey(key))
            {
                dict[key] = new List<string>();
            }
            dict[key].AddRange(dir[key]);
        }
    }
    // Return results
    return dict;
}

Let's test it briefly and modify the Main method:

static void Main(string[] args)
{
    var drivers = GetDrivers();
    var results = OverDirectories(drivers[0]);
    Console.WriteLine(results);
}

Well, if there's no accident, you should get the following tips:

This is because there will be some files or directories in the system (no matter which system) that we do not have permission to access. In this case, we must use try/catch to process these directories and files without permission. Because we usually don't put files under these directories, we can simply skip these directories.

At the same time, observe that GetDrivers returns a set of DirectoryInfo instances, while OverDirectories processes one directory at a time, and then returns a dictionary set, so we need to integrate these sets, but we have written similar code in OverDirectories. In order to reduce the number of repeated code, we can extract this code as a method:

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 yes C#Variable parameter list keyword, declaration method: params T[] values. The expression method can receive any T-type parameter, and the method receives an array

Continue to modify the OverDirectories method and add exception handling:

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($"Error message:{e}");//Print error messages
    }

    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($"Error message:{e}");//Print error messages
    }
    return dict;
}

Finally, modify the Main method to support query using the string entered by the user:

static void Main(string[] args)
{
    var drivers = GetDrivers();
    var results = Concat(drivers.Select(OverDirectories).ToArray());
    Console.WriteLine("Please enter the file name to query:");
    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("The path found is:");
        foreach(var path in list)
        {
            Console.WriteLine(path);
        }
    }
}

3. Summary

This is the code. It can be said that the basic functions have been completed. If there are small partners trying to use the sample code, they may encounter various problems. In the next article, we will continue to optimize it based on the existing knowledge, so that it can become a real small tool.

Please pay attention to more My blog Mr. Gao's Cabin

Keywords: C# Windows Linux Mac Programming

Added by bampot on Thu, 07 May 2020 08:46:29 +0300