如何在 C# 中实现 glob

发布于 2024-07-11 02:59:52 字数 100 浏览 6 评论 0原文

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

I don't know if it's legit at StackOverflow to post your own answer to a question, but I saw nobody had asked this already. I went looking for a C# Glob and didn't find one, so I wrote one that others might find useful.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

北城挽邺 2024-07-18 02:59:52
    /// <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);
    }
    /// <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);
    }
淡忘如思 2024-07-18 02:59:52

您可以使用 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();
        }

    }
}

You can use the "dir" (aka "Get-ChildItem") powershell cmdlet from C#.
(I'm not saying whether you should.)

You have to add this reference to your project file (".csproj" or ".vcproj") manually:

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

See here for more details on how to use cmdlets from C#: http://www.devx.com/tips/Tip/42716

Here a working program:

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();
        }

    }
}
放赐 2024-07-18 02:59:52

使用 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;
            }
        }
    }
}

It is easy to implement with DotNet.Glob

Example:

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;
            }
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文