【问题标题】:How to implement glob in C#如何在 C# 中实现 glob
【发布时间】:2010-09-28 18:00:50
【问题描述】:

我不知道在 StackOverflow 上发布您自己的问题答案是否合法,但我看到没有人问过这个问题。我去找了一个 C# Glob,但没有找到,所以我写了一个,其他人可能会觉得有用。

【问题讨论】:

  • 经过一些 google-ling 我发现 glob 应该做什么。 en.wikipedia.org/wiki/Glob_(programming)
  • 如果你没有把它变成一个社区维基,你会得到更多的积分。 :-)
  • 为什么我会得到更多的积分?我是新来的……
  • 仅供参考:Glob 看起来像 path***.txt
  • @Mark 因为社区 Wiki 的回答不会奖励积分,每次点赞通常都会给你 10 分。

标签: c# glob wildcard


【解决方案1】:
    /// <summary>
    /// return a list of files that matches some wildcard pattern, e.g. 
    /// C:\p4\software\dotnet\tools\*\*.sln to get all tool solution files
    /// </summary>
    /// <param name="glob">pattern to match</param>
    /// <returns>all matching paths</returns>
    public static IEnumerable<string> Glob(string glob)
    {
        foreach (string path in Glob(PathHead(glob) + DirSep, PathTail(glob)))
            yield return path;
    }

    /// <summary>
    /// uses 'head' and 'tail' -- 'head' has already been pattern-expanded
    /// and 'tail' has not.
    /// </summary>
    /// <param name="head">wildcard-expanded</param>
    /// <param name="tail">not yet wildcard-expanded</param>
    /// <returns></returns>
    public static IEnumerable<string> Glob(string head, string tail)
    {
        if (PathTail(tail) == tail)
            foreach (string path in Directory.GetFiles(head, tail).OrderBy(s => s))
                yield return path;
        else
            foreach (string dir in Directory.GetDirectories(head, PathHead(tail)).OrderBy(s => s))
                foreach (string path in Glob(Path.Combine(head, dir), PathTail(tail)))
                    yield return path;
    }

    /// <summary>
    /// shortcut
    /// </summary>
    static char DirSep = Path.DirectorySeparatorChar;

    /// <summary>
    /// return the first element of a file path
    /// </summary>
    /// <param name="path">file path</param>
    /// <returns>first logical unit</returns>
    static string PathHead(string path)
    {
        // handle case of \\share\vol\foo\bar -- return \\share\vol as 'head'
        // because the dir stuff won't let you interrogate a server for its share list
        // FIXME check behavior on Linux to see if this blows up -- I don't think so
        if (path.StartsWith("" + DirSep + DirSep))
            return path.Substring(0, 2) + path.Substring(2).Split(DirSep)[0] + DirSep + path.Substring(2).Split(DirSep)[1];

        return path.Split(DirSep)[0];
    }

    /// <summary>
    /// return everything but the first element of a file path
    /// e.g. PathTail("C:\TEMP\foo.txt") = "TEMP\foo.txt"
    /// </summary>
    /// <param name="path">file path</param>
    /// <returns>all but the first logical unit</returns>
    static string PathTail(string path)
    {
        if (!path.Contains(DirSep))
            return path;

        return path.Substring(1 + PathHead(path).Length);
    }

【讨论】:

  • 错误?我不得不用“dir”替换“Path.Combine(head, dir)”,因为 Directory.GetDirectories 已经返回了完整路径。这会导致路径如“..\SomeDir*.dll”的错误,因为“..\”被 Combine 复制了
  • 如果您将* 之类的字符串传递给Glob 函数,这似乎不起作用。是否对它可以处理的通配符字符串进行了一些假设?也许是绝对路径?
  • 方法GlobDirSep 处将参数分成两部分。如果没有Dirsep,代码将失败。将以下语句添加到方法 PathHead 的开头似乎可以工作:if (! path.Contains(DirSep)) {return ".";}
  • @Ben 假设似乎字符串包含DirSep。随着我之前评论的更改,代码对我有用。
【解决方案2】:

我偶然发现了铁红宝石的来源,其中包含一个非常简洁的 Glob 类。从相关代码中提取它相当容易。

https://github.com/IronLanguages/main/blob/master/Languages/Ruby/Ruby/Builtins/Glob.cs

【讨论】:

    【解决方案3】:

    您可以使用 C# 中的“dir”(又名“Get-ChildItem”)powershell cmdlet。
    (我不是说你是否应该这样做。)

    您必须手动将此引用添加到您的项目文件(“.csproj”或“.vcproj”):

    <Reference Include="System.Management.Automation" />
    

    有关如何在 C# 中使用 cmdlet 的更多详细信息,请参见此处:http://www.devx.com/tips/Tip/42716

    这里是一个工作程序:

    using System;
    using System.Collections.Generic;
    
    using System.Management.Automation;
    using System.Management.Automation.Runspaces;
    using System.Collections.ObjectModel;
    
    namespace CsWildcard {
        class Program {
    
            static IEnumerable<string> CmdletDirGlobbing(string basePath, string glob){
                Runspace runspace = RunspaceFactory.CreateRunspace();
                runspace.Open();
    
                // cd to basePath
                if(basePath != null){
                    Pipeline cdPipeline = runspace.CreatePipeline();
                    Command cdCommand = new Command("cd");
                    cdCommand.Parameters.Add("Path", basePath);
                    cdPipeline.Commands.Add(cdCommand);
                    cdPipeline.Invoke(); // run the cmdlet
                }
    
                // run the "dir" cmdlet (e.g. "dir C:\*\*\*.txt" )
                Pipeline dirPipeline = runspace.CreatePipeline();
                Command dirCommand = new Command("dir");
                dirCommand.Parameters.Add("Path", glob);
                dirPipeline.Commands.Add(dirCommand);
    
                Collection<PSObject> dirOutput = dirPipeline.Invoke();
    
                // for each found file
                foreach (PSObject psObject in dirOutput) {
    
                    PSMemberInfoCollection<PSPropertyInfo> a = psObject.Properties;
                    // look for the full path ("FullName")
                    foreach (PSPropertyInfo psPropertyInfo in psObject.Properties) {
                        if (psPropertyInfo.Name == "FullName") {
                            yield return psPropertyInfo.Value.ToString(); // yield it
                        }
                    }
                }
    
            }
    
            static void Main(string[] args) {
                foreach(string path in CmdletDirGlobbing(null,"C:\\*\\*\\*.txt")){
                    System.Console.WriteLine(path);
                }
                foreach (string path in CmdletDirGlobbing("C:\\", "*\\*\\*.exe")) {
                    System.Console.WriteLine(path);
                }   
                Console.ReadKey();
            }
    
        }
    }
    

    【讨论】:

      【解决方案4】:

      https://github.com/dazinator/DotNet.Glob 很容易:

      示例:

      public static class Glob
      {
        public static IEnumerable<FileInfo> Exec(DirectoryInfo dir, string glob)
        {
          var matcher = DotNet.Globbing.Glob.Parse(glob);
          return dir.EnumerateAllFiles().Where(f => matcher.IsMatch(f.FullName));
        }
      
        public static IEnumerable<FileInfo> EnumerateAllFiles(this DirectoryInfo dir)
        {
          foreach (var f in dir.EnumerateFiles())
          {
            yield return f;
          }
      
          foreach (var sub in dir.EnumerateDirectories())
          {
            foreach (var f in EnumerateAllFiles(sub))
            {
              yield return f;
            }
          }
        }
      }
      

      【讨论】:

      • 您从哪里获得 DirectoryInfo?如果我只有一个字符串 glob,我不想编写逻辑来拉出基本目录...
      • 您可以拥有自己的版本,其中基本目录只是正在运行的进程的当前目录。还可以轻松扩展示例以支持多个基本目录。
      • “DirectoryInfo”不包含“EnumerateAllFiles”的定义错误。
      • @vee EnumerateAllFiles 扩展方法在我的 sn-p 的 Glob 类中定义。您可以将其更改为非扩展版本。我不确定您为什么会收到此错误。
      猜你喜欢
      • 2017-05-24
      • 2021-08-18
      • 2016-04-13
      • 2014-10-29
      • 2018-02-14
      • 2011-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多