Бөлісу құралы:


Глоббирование файлов в .NET

В этой статье вы узнаете, как использовать подстановку имен файлов с пакетом NuGet 📦 Microsoft.Extensions.FileSystemGlobbing. Глоб-выражение — это термин, который используется для описания шаблонов для сопоставления имен файлов и каталогов на основе подстановочных знаков. Глоббинг — это процесс определения одного или нескольких шаблонов глобов и получения файлов из включительных или исключительных совпадений.

Шаблоны

Чтобы сопоставить файлы в файловой системе на основе определяемых пользователем шаблонов, начните с создания экземпляра объекта Matcher. Можно создать экземпляр Matcher без параметров или с параметром System.StringComparison, который используется внутренне для сравнения шаблонов с именами файлов. Matcher предоставляет следующие аддитивные методы:

И метод AddExclude, и AddInclude можно вызывать любое количество раз, чтобы добавлять различные шаблоны имен файлов, исключаемые из результатов или включаемые в них. После создания экземпляра Matcher и добавления шаблонов, он используется для вычисления совпадений из начального каталога с помощью метода Matcher.Execute.

Методы расширения

Объект Matcher имеет несколько методов расширения.

Множественные исключения

Чтобы добавить несколько шаблонов исключения, можно использовать:

Matcher matcher = new();
matcher.AddExclude("*.txt");
matcher.AddExclude("*.asciidoc");
matcher.AddExclude("*.md");

Кроме того, можно использовать MatcherExtensions.AddExcludePatterns(Matcher, IEnumerable<String>[]) для добавления нескольких шаблонов исключения в один вызов:

Matcher matcher = new();
matcher.AddExcludePatterns(new [] { "*.txt", "*.asciidoc", "*.md" });

Этот метод расширения выполняет итерацию всех предоставленных шаблонов, вызывая AddExclude от вашего имени.

Множественные включения

Чтобы добавить несколько шаблонов включения, можно использовать:

Matcher matcher = new();
matcher.AddInclude("*.txt");
matcher.AddInclude("*.asciidoc");
matcher.AddInclude("*.md");

Кроме того, можно использовать MatcherExtensions.AddIncludePatterns(Matcher, IEnumerable<String>[]) для добавления нескольких шаблонов включения в один вызов:

Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "*.txt", "*.asciidoc", "*.md" });

Этот метод расширения выполняет итерацию всех предоставленных шаблонов, вызывая AddInclude от вашего имени.

Получить все соответствующие файлы

Чтобы получить все соответствующие файлы, необходимо вызвать Matcher.Execute(DirectoryInfoBase), напрямую или косвенно. Для непосредственного вызова вам потребуется каталог поиска:

Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "*.txt", "*.asciidoc", "*.md" });

string searchDirectory = "../starting-folder/";

PatternMatchingResult result = matcher.Execute(
    new DirectoryInfoWrapper(
        new DirectoryInfo(searchDirectory)));

// Use result.HasMatches and results.Files.
// The files in the results object are file paths relative to the search directory.

В приведенном выше коде C#:

  • Создает объект Matcher.
  • Вызывает AddIncludePatterns(Matcher, IEnumerable<String>[]), чтобы добавить несколько шаблонов имен файлов для включения.
  • Объявляет и присваивает значение каталога поиска.
  • Создает экземпляр DirectoryInfo из заданного searchDirectory.
  • Создает экземпляр DirectoryInfoWrapper из DirectoryInfo, оболочкой которого он является.
  • Вызывает Execute, которому передан экземпляр DirectoryInfoWrapper для получения объекта PatternMatchingResult.

Примечание.

Тип DirectoryInfoWrapper определен в пространстве имен Microsoft.Extensions.FileSystemGlobbing.Abstractions, а тип DirectoryInfo определен в пространстве имен System.IO. Чтобы избежать ненужных using директив, можно использовать предоставленные методы расширения.

Существует другой метод расширения, который возвращает IEnumerable<string>, представляющий соответствующие файлы:

Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "*.txt", "*.asciidoc", "*.md" });

string searchDirectory = "../starting-folder/";

IEnumerable<string> matchingFiles = matcher.GetResultsInFullPath(searchDirectory);

// Use matchingFiles if there are any found.
// The files in this collection are fully qualified file system paths.

В приведенном выше коде C#:

  • Создает объект Matcher.
  • Вызывает AddIncludePatterns(Matcher, IEnumerable<String>[]), чтобы добавить несколько шаблонов имен файлов для включения.
  • Объявляет и присваивает значение каталога поиска.
  • Вызывает GetResultsInFullPath с заданным значением searchDirectory для получения всех соответствующих файлов в виде IEnumerable<string>.

Перегрузка функций

Объект PatternMatchingResult представляет коллекцию экземпляров FilePatternMatch и предоставляет значение, указывающее, совпадает ли результат с результатомboolean.PatternMatchingResult.HasMatches

С помощью экземпляра Matcher можно вызвать любую из различных перегрузок Match, чтобы получить результат сопоставления шаблона. Методы Match перекладывают на вызывающий объект ответственность за предоставление файла или коллекции файлов, которые должны оцениваться на предмет совпадений. Иными словами, вызывающая сторона отвечает за передачу файла для сопоставления.

Внимание

При использовании любой из перегрузок Match операции ввода-вывода файловой системы не используются. Глоббинг файлов полностью выполняется в памяти с использованием шаблонов включения и исключения для экземпляра matcher. Параметры перегрузок Match не обязательно должны содержать полные пути. Если ничего не указано, используется текущий каталог (Directory.GetCurrentDirectory()).

Чтобы сопоставить один файл:

Matcher matcher = new();
matcher.AddInclude("**/*.md");

PatternMatchingResult result = matcher.Match("file.md");

В приведенном выше коде C#:

  • Соответствует любому файлу с расширением .md на произвольной глубине каталога.
  • Если файл с именем file.md существует в подкаталоге из текущего каталога:
    • result.HasMatches будет true.
    • А result.Files будет иметь одно совпадение.

Дополнительные перегрузки Match работают аналогичным образом.

Упорядоченная оценка включения и исключения

По умолчанию сопоставитель сначала обрабатывает все шаблоны включения, затем применяет все шаблоны исключения, независимо от порядка их добавления. Это означает, что вы не можете повторно включать файлы, которые ранее были исключены.

Начиная с версии 10 📦 пакета Microsoft.Extensions.FileSystemGlobbing, можно выбрать упорядоченную оценку, где в том числе и исключения обрабатываются точно в последовательности, в которой они были добавлены:

using Microsoft.Extensions.FileSystemGlobbing;

// Preserve the order of patterns when matching.
Matcher matcher = new(preserveFilterOrder: true);

matcher.AddInclude("**/*");                // include everything
matcher.AddExclude("logs/**/*");           // exclude logs
matcher.AddInclude("logs/important/**/*"); // re-include important logs

var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(root)));
foreach (var file in result.Files)
{
    Console.WriteLine(file.Path);
}

В этом режиме шаблоны применяются друг за другом:

  • **/* добавляет все файлы.
  • logs/**/* отфильтровывает все в logs/.
  • logs/important/**/* добавляет обратно только файлы под logs/important/.

Существующий код, использующий конструктор по умолчанию, будет продолжать выполняться с исходным поведением "сначала все включения, затем все исключения".

Форматы шаблонов

Шаблоны, указанные в методах AddExclude и AddIncludeмогут использовать следующие форматы для сопоставления нескольких файлов или каталогов.

  • Точное имя каталога или файла

    • some-file.txt
    • path/to/file.txt
  • Подстановочные знаки * в именах файлов и каталогов, представляющих ноль и многие символы, не включая символы-разделители.

    значение Описание
    *.txt Все файлы с расширением .txt.
    *.* Все файлы с расширением.
    * Все файлы в каталоге верхнего уровня.
    .* Имена файлов, начинающиеся с ".".
    *word* Все файлы с "word" в имени файла.
    readme.* Все файлы с именем "readme" и любым расширением файла.
    styles/*.css Все файлы с расширением ".css" в каталоге "styles/".
    scripts/*/* Все файлы в папке "scripts/" или в одном уровне вложенной папки под "scripts/".
    images*/* Все файлы в папке с именем "images" или именем, начинающимся с "images".
  • Произвольная глубина каталога (/**/).

    значение Описание
    **/* Все файлы в любом подкаталоге.
    dir/ Все файлы в любом подкаталоге каталога "dir/".
    dir/**/* Все файлы в любом подкаталоге каталога "dir/".
  • Относительные пути.

    Чтобы сопоставить все файлы в каталоге с именем "shared" на уровне, параллельном базовому каталогу, предоставленному Matcher.Execute(DirectoryInfoBase), используйте ../shared/*.

Примеры

Рассмотрим следующий пример каталога и каждый файл в его соответствующей папке.

📁 parent
│    file.md
│    README.md
│
└───📁 child
    │    file.MD
    │    index.js
    │    more.md
    │    sample.mtext
    │
    ├───📁 assets
    │        image.png
    │        image.svg
    │
    └───📁 grandchild
             file.md
             style.css
             sub.text

Совет

Некоторые расширения файлов находятся в верхнем регистре, а другие — в нижнем регистре. По умолчанию используется StringComparer.OrdinalIgnoreCase. Чтобы указать другое поведение сравнения строк, используйте конструктор Matcher.Matcher(StringComparison).

Для получения всех Markdown файлов с расширением .md или .mtext, независимо от регистра символов:

Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "**/*.md", "**/*.mtext" });

foreach (string file in matcher.GetResultsInFullPath("parent"))
{
    Console.WriteLine(file);
}

При запуске приложения выводятся результаты, аналогичные приведенным ниже:

C:\app\parent\file.md
C:\app\parent\README.md
C:\app\parent\child\file.MD
C:\app\parent\child\more.md
C:\app\parent\child\sample.mtext
C:\app\parent\child\grandchild\file.md

Чтобы получить любые файлы в каталоге assets с произвольной глубиной:

Matcher matcher = new();
matcher.AddInclude("**/assets/**/*");

foreach (string file in matcher.GetResultsInFullPath("parent"))
{
    Console.WriteLine(file);
}

При запуске приложения выводятся результаты, аналогичные приведенным ниже:

C:\app\parent\child\assets\image.png
C:\app\parent\child\assets\image.svg

Для получения файлов, у которых имя каталога содержит слово child, с произвольной глубиной и расширениями которых не являются .md, .text или .mtext:

Matcher matcher = new();
matcher.AddInclude("**/*child/**/*");
matcher.AddExcludePatterns(
    new[]
    {
        "**/*.md", "**/*.text", "**/*.mtext"
    });

foreach (string file in matcher.GetResultsInFullPath("parent"))
{
    Console.WriteLine(file);
}

При запуске приложения выводятся результаты, аналогичные приведенным ниже:

C:\app\parent\child\index.js
C:\app\parent\child\assets\image.png
C:\app\parent\child\assets\image.svg
C:\app\parent\child\grandchild\style.css

См. также