Globbing de arquivo no .NET
Neste artigo, você aprenderá a usar o recurso de curinga (globbing) de arquivo com o pacote NuGet Microsoft.Extensions.FileSystemGlobbing
. Um glob é um termo usado para definir padrões de correspondência de nomes de arquivo e diretório com base em curingas. Globbing é o ato de definir um ou mais padrões glob e produzir arquivos com base em correspondências inclusivas ou exclusivas.
Padrões
Para corresponder arquivos no sistema de arquivos com base em padrões definidos pelo usuário, comece instanciando um objeto Matcher. Um Matcher
pode ser instanciado sem parâmetros ou com um parâmetro System.StringComparison, usado internamente para comparar padrões com nomes de arquivo. O Matcher
expõe os seguintes métodos aditivos:
Os métodos AddExclude
e AddInclude
podem ser chamados várias vezes para adicionar vários padrões de nome de arquivo para excluir ou incluir nos resultados. Depois de criar uma instância de Matcher
e adicionar padrões, ele será usado para avaliar correspondências de um diretório inicial com o método Matcher.Execute.
Métodos de extensão
O objeto Matcher
tem vários métodos de extensão.
Várias exclusões
Para adicionar vários padrões de exclusão, você pode usar:
Matcher matcher = new();
matcher.AddExclude("*.txt");
matcher.AddExclude("*.asciidoc");
matcher.AddExclude("*.md");
Como alternativa, você pode usar MatcherExtensions.AddExcludePatterns(Matcher, IEnumerable<String>[]) para adicionar vários padrões de exclusão em uma única chamada:
Matcher matcher = new();
matcher.AddExcludePatterns(new [] { "*.txt", "*.asciidoc", "*.md" });
Esse método de extensão itera em todos os padrões fornecidos chamando AddExclude em seu nome.
Várias inclusões
Para adicionar vários padrões de inclusão, você pode usar:
Matcher matcher = new();
matcher.AddInclude("*.txt");
matcher.AddInclude("*.asciidoc");
matcher.AddInclude("*.md");
Como alternativa, você pode usar MatcherExtensions.AddIncludePatterns(Matcher, IEnumerable<String>[]) para adicionar vários padrões de inclusão em uma única chamada:
Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "*.txt", "*.asciidoc", "*.md" });
Esse método de extensão itera em todos os padrões fornecidos chamando AddInclude em seu nome.
Obter todos os arquivos correspondentes
Para obter todos os arquivos correspondentes, você precisa chamar Matcher.Execute(DirectoryInfoBase) direta ou indiretamente. Para chamar diretamente, você precisa de um diretório de pesquisa:
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.
O código anterior do C#:
- Instancia um objeto Matcher.
- Chama AddIncludePatterns(Matcher, IEnumerable<String>[]) para adicionar vários padrões de nome de arquivo a serem incluídos.
- Declara e atribui o valor do diretório de pesquisa.
- Cria uma instância de DirectoryInfo pelo
searchDirectory
fornecido. - Cria uma instância de DirectoryInfoWrapper pelo
DirectoryInfo
encapsulado. - Chama
Execute
com a instânciaDirectoryInfoWrapper
para produzir um objeto PatternMatchingResult.
Observação
O tipo DirectoryInfoWrapper
é definido no namespace Microsoft.Extensions.FileSystemGlobbing.Abstractions
e o tipo DirectoryInfo
é definido no namespace System.IO
. Para evitar diretivas using
desnecessárias, você pode usar os métodos de extensão fornecidos.
Há outro método de extensão que gera uma IEnumerable<string>
representando os arquivos correspondentes:
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.
O código anterior do C#:
- Instancia um objeto Matcher.
- Chama AddIncludePatterns(Matcher, IEnumerable<String>[]) para adicionar vários padrões de nome de arquivo a serem incluídos.
- Declara e atribui o valor do diretório de pesquisa.
- Chama
GetResultsInFullPath
com o valorsearchDirectory
para produzir todos os arquivos correspondentes comoIEnumerable<string>
.
Corresponder sobrecargas
O objeto PatternMatchingResult representa uma coleção de instâncias de FilePatternMatch e expõe um valor boolean
que indica se o resultado tem correspondências (PatternMatchingResult.HasMatches).
Com uma instância de Matcher
, você pode chamar qualquer uma das várias sobrecargas Match
para obter um resultado de correspondência de padrões. Os métodos Match
invertem a responsabilidade no chamador para fornecer um arquivo ou uma coleção de arquivos nos quais as correspondências serão avaliadas. Em outras palavras, o chamador é responsável por passar o arquivo para corresponder.
Importante
Ao usar qualquer uma das sobrecargas Match
, não há uma E/S do sistema de arquivos envolvida. Toda a globbing de arquivo é feita na memória com os padrões de inclusão e exclusão da instância de matcher
. Os parâmetros das sobrecargas Match
não precisam ser caminhos totalmente qualificados. O diretório atual (Directory.GetCurrentDirectory()) é usado, quando não especificado.
Para corresponder um único arquivo:
Matcher matcher = new();
matcher.AddInclude("**/*.md");
PatternMatchingResult result = matcher.Match("file.md");
O código anterior do C#:
- Corresponde a qualquer arquivo com a extensão .md, em uma profundidade de diretório arbitrária.
- Se houvesse um arquivo chamado file.md em um subdiretório do diretório atual:
result.HasMatches
seriatrue
.- e
result.Files
teria uma correspondência.
As sobrecargas Match
adicionais funcionam de maneiras semelhantes.
Formatos de padrão
Os padrões especificados nos métodos AddExclude
e AddInclude
podem usar os formatos a seguir para fazer a correspondência de vários arquivos ou diretórios.
Diretório ou nome do arquivo exato
some-file.txt
path/to/file.txt
Curingas
*
em nomes de arquivo e diretório que representem zero para muitos caracteres não incluem caracteres separadores.Valor Descrição *.txt
Todos os arquivos com extensão .txt. *.*
Todos os arquivos com extensão. *
Todos os arquivos no diretório de nível superior. .*
Nomes de arquivo começando com “.”. *word*
Todos os arquivos com “word” no nome do arquivo. readme.*
Todos os arquivos chamados “readme” com qualquer extensão de arquivo. styles/*.css
Todos os arquivos com a extensão “.css” no diretório “styles/”. scripts/*/*
Todos os arquivos em “scripts/” ou um nível de subdiretório em “scripts/”. images*/*
Todos os arquivos em uma pasta cujo nome que é ou começa com “images”. Profundidade arbitrária do diretório (
/**/
).Valor Descrição **/*
Todos os arquivos em qualquer subdiretório. dir/
Todos os arquivos em qualquer subdiretório em “dir/”. dir/**/*
Todos os arquivos em qualquer subdiretório em “dir/”. Caminhos relativos.
Para corresponder todos os arquivos em um diretório chamado "shared" no nível irmão com o diretório base dado a Matcher.Execute(DirectoryInfoBase), use
../shared/*
.
Exemplos
Considere o diretório de exemplo a seguir e cada arquivo na sua pasta correspondente.
📁 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
Dica
Algumas extensões de arquivo estão em letras maiúsculas, enquanto outras estão em letras minúsculas. Por padrão, StringComparer.OrdinalIgnoreCase é usado. Para especificar diferentes comportamentos de comparação de cadeia de caracteres, use o construtor Matcher.Matcher(StringComparison).
Para obter todos os arquivos markdown, em que a extensão é .md ou .mtext, independentemente de estar em maiúsculas ou e minúsculas:
Matcher matcher = new();
matcher.AddIncludePatterns(new[] { "**/*.md", "**/*.mtext" });
foreach (string file in matcher.GetResultsInFullPath("parent"))
{
Console.WriteLine(file);
}
Executar o aplicativo produziria resultados semelhantes aos seguintes:
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
Para obter arquivos em um diretório de assets em uma profundidade arbitrária:
Matcher matcher = new();
matcher.AddInclude("**/assets/**/*");
foreach (string file in matcher.GetResultsInFullPath("parent"))
{
Console.WriteLine(file);
}
Executar o aplicativo produziria resultados semelhantes aos seguintes:
C:\app\parent\child\assets\image.png
C:\app\parent\child\assets\image.svg
Para obter arquivos em que o nome do diretório contém a palavra child em uma profundidade arbitrária e as extensões de arquivo não são .md, .text ou .mtext:
Matcher matcher = new();
matcher.AddInclude("**/*child/**/*");
matcher.AddExcludePatterns(
new[]
{
"**/*.md", "**/*.text", "**/*.mtext"
});
foreach (string file in matcher.GetResultsInFullPath("parent"))
{
Console.WriteLine(file);
}
Executar o aplicativo produziria resultados semelhantes aos seguintes:
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