Как реализовать glob на C#

Я не знаю, законно ли в StackOverflow публиковать свой ответ на вопрос, но я видел, что никто этого уже не задавал. Я искал C# Glob и не нашел его, поэтому написал тот, который другим может быть полезен.

После некоторого google-ling я обнаружил, что должен делать glob. en.wikipedia.org/wiki/Glob_(programming)

tuinstoel 29.12.2008 23:41

Вы бы получили больше очков, если бы не сделали это вики сообщества. :-)

George Stocker 30.12.2008 04:10

Зачем мне было набирать больше баллов? Я здесь новенький ...

Mark Maxham 02.01.2009 06:27

Для справки: глобусы выглядят как path ***. Txt

Daniel Little 19.12.2014 01:42

@Mark, потому что ответы вики сообщества не приносят баллов, каждый голос обычно дает вам 10 баллов.

Daniel Little 19.12.2014 01:42
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
19
5
9 578
4

Ответы 4

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

Ошибка? Мне пришлось заменить «Path.Combine (head, dir)» на «dir», поскольку Directory.GetDirectories уже возвращает полный путь. Это вызвало ошибку с такими путями, как ".. \ SomeDir * .dll", поскольку ".. \" были дублированы Combine

jturcotte 05.03.2009 19:08

Это не сработает, если вы передадите строку типа * в функцию Glob. Есть ли какие-то предположения относительно типа строки с подстановочными знаками, которую он может обрабатывать? Может быть, абсолютный путь?

Ben 30.09.2011 15:06

Метод Glob разбивает аргумент на две части в DirSep. Код не работает, если нет Dirsep. Добавление следующего оператора в начало метода PathHead, похоже, работает: if (! path.Contains(DirSep)) {return ".";}.

AdrianHHH 30.07.2015 14:18

@Ben Предполагается, что строка содержит DirSep. С изменением в моем предыдущем комментарии код работает для меня.

AdrianHHH 17.08.2015 12:29

Я наткнулся на источник железного рубина, который содержит довольно аккуратный класс Glob. Его довольно легко извлечь из связанного кода.

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

Вы можете использовать командлет PowerShell "dir" (он же "Get-ChildItem") из C# .
(Я не говорю, следует ли вам.)

Вы должны добавить эту ссылку в файл вашего проекта (".csproj "или".vcproj") вручную:

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

Подробнее о том, как использовать командлеты из C#, см. Здесь: 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();
        }

    }
}

это легко с 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? Если у меня есть просто строковый глобус, я не хочу писать логику для извлечения базового каталога ...

jjxtra 14.04.2020 00:59

У вас может быть собственная версия, в которой базовый каталог - это просто текущий каталог запущенного процесса. Также пример можно легко расширить для поддержки нескольких базовых каталогов.

sergeyt 15.04.2020 07:07

Ошибка DirectoryInfo не содержит определения для EnumerateAllFiles..

vee 11.01.2021 15:30

Метод расширения @vee EnumerateAllFiles определен в классе Glob в моем фрагменте. Вы можете изменить его на версию без расширения. Я не уверен, почему вы получаете эту ошибку.

sergeyt 17.01.2021 17:13

Другие вопросы по теме