c# - 如何从可能包含驱动器号和/或通配符的 args 构建目录列表

标签 c#

我想将目录列表作为参数传递到我的 C# 控制台应用程序,这些参数可能是基于当前工作目录的相对路径,或者包含驱动器号和/或通配符。例如:

myApp.exe someDir another\dir dirs\with\wild*cards* ..\..\a\relative\dir C:\an\absolute\dir\with\wild*cards*

到目前为止,我已经想到了这个:

static void Main(string[] args)
{
    List<string> directories = new List<string>();
    foreach (string arg in args)
    {
        if (Directory.Exists(arg))
        {
           // 'arg' by itself is a valid directory, and must not contain any wildcards
           // just add it to 'directories'
           directories.Add(arg);
        }
        else
        {
           // 'arg' must either be a non-existant directory or invalid directory name,
           // or else it contains wildcard(s). Find all matching directory names, starting
           // at the current directory (assuming 'arg' might be a relative path), and add
           // all matching directory names to 'directories'.
           string[] dirs = Directory.GetDirectories(Directory.GetCurrentDirectory(), arg, SearchOption.TopDirectoryOnly);
           directories.AddRange(dirs);
        }
     }

     Console.WriteLine("Full list of directories specified by the command line args:");
     foreach (string dir in directories)
     {
        Console.WriteLine("    " + dir);
     }

     // Now go do what I want to do for each of these directories...
}

这适用于 someDiranother\dirdirs\with\wild*cards*,但不适用于..\..\a\relative\dirC:\an\abolute\dir\with\wild*cards*。对于相对目录,Directory.GetDirectories() 抛出一个 System.ArgumentException 说“搜索模式不能包含‘..’以向上移动目录并且只能包含在内部文件/目录名称,如'a..b'。”对于基于驱动器号的目录,它会抛出一个 System.ArgumentException 说“第二个路径片段不能是驱动器或 UNC 名称。”

如何处理“..”和驱动器号?这必须是一个已解决的问题,但我找不到 C# 的此类示例。

“奖励问题”:我上面的代码也不处理 a\path\with\wild*cards*\in\anything\other\than\the\top\directory。还有什么简单的方法可以处理吗?

最佳答案

一些观察:

1) 要检查路径是绝对路径还是相对路径 - 您可以使用 Path.IsRooted()

2) 要将带有“..”的路径解析为绝对路径(无论是相对路径还是绝对路径),您可以使用:

path = new Uri(Path.Combine(Directory.GetCurrentDirectory(), path)).AbsolutePath;

通过 Uri 路由它会扩展这些点。 Path.GetFullPath 在使用通配符时会失败,但 Uri 不会。

3) 要检查路径是否包含通配符,您只需执行 path.Contains("*") || path.Contains("?") 因为这两个字符都不是有效的路径字符,所以除了作为通配符之外不能出现在上下文中。

4) 要解析绝对路径中的通配符(并匹配您的“奖励”要求),您需要找出第一个不包含通配符的目录。所以你基本上需要将路径分成几个部分 - 在第一个通配符之前和第一个通配符之后。例如:

C:\an\abolute\dir\with\wild*cards*

通配符之前的路径是 C:\an\abolute\dir\with,在通配符之后(包括):wild*cards*

C:\an\abolu*e\dir\with\wild*cards*

第一个通配符之前的路径:C:\an,之后的路径:abolu*e\dir\with\wild*cards*。当然有不同的方法,我认为最简单的是正则表达式:

@"[\\/](?=[^\\/]*[\*?])"

它基本上匹配目录分隔符,但前提是它后面跟着 0 个或更多不是目录分隔符的字符,然后是通配符。

将所有这些结合在一起,我们有:

static IEnumerable<string> ResolveDirectories(string path) {
    if (path.Contains("*") || path.Contains("?")) {
        // we have a wildcard,
        // resolve it to absolute path if necessary
        if (!Path.IsPathRooted(path))
            path = Path.Combine(Directory.GetCurrentDirectory(), path);
        // resolve .. stuff if any
        path = new Uri(Path.Combine(Directory.GetCurrentDirectory(), path)).AbsolutePath;
        // split with regex above, only on first match (2 parts)
        var parts = new Regex(@"[\\/](?=[^\\/]*[\*?])").Split(path, 2);
        var searchRoot = parts[0];                
        var searchPatterns = parts[1].Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
        foreach (var dir in ResolveWildcards(searchRoot, searchPatterns))
            yield return dir;                
    }
    else {
        // this should work with "../.." type of paths too, will resolve relative to current directory
        // so no processing is necessary
        if (Directory.Exists(path)) {
            yield return path;
        }
        else {
            // invalid directory?
        }
    }
}

static IEnumerable<string> ResolveWildcards(string searchRoot, string [] searchPatterns) {
    // if we have path C:\an\abolu*e\dir\with\wild*cards*
    // search root is C:\an
    // and patterns are: [abolu*e, dir, with, wild*cards*]
    if (Directory.Exists(searchRoot)) {
        // use next pattern to search in a search root
        var next = searchPatterns[0];
        // leave the rest for recursion
        var rest = searchPatterns.Skip(1).ToArray();
        foreach (var dir in Directory.EnumerateDirectories(searchRoot, next, SearchOption.AllDirectories)) {
            // if nothing left (last pattern) - return it
            if (rest.Length == 0)
                yield return dir;
            else {
                // otherwise search with rest patterns in freshly found directory
                foreach (var sub in ResolveWildcards(dir, rest))
                    yield return sub;
            }
        }
    }
}

这没有经过适当的测试,所以要小心。

关于c# - 如何从可能包含驱动器号和/或通配符的 args 构建目录列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49656645/

相关文章:

c# - C#中将String XML转成数据表

c# - WPF Canvas 性能 - 多次调用 children.add

c# - .NET 4.5 超过 4.0 时大量增加了新的 TCP 连接数?

c# - 如何配置 NLog 以写入 Azure 应用服务的日志流?

c# - 如何使用多个依赖属性绑定(bind)到计算属性

C# Montouch/Xamarin - 相机快门速度

c# - C# '??' 运算符线程安全吗?

c# - 使用 NAudio 将 WAV 流转换为 Opus

c# - 发送电子邮件而不在已发送文件夹中留下副本 outlook c#

c# - 通过 DataAdapter 删除行