SearchPattern de várias extensões de arquivo para System.IO.Directory.GetFiles

140

Qual é a sintaxe para definir várias extensões de arquivo como searchPatternativadas Directory.GetFiles()? Por exemplo, filtrando arquivos com extensões .aspx e .ascx .

// TODO: Set the string 'searchPattern' to only get files with
// the extension '.aspx' and '.ascx'.
var filteredFiles = Directory.GetFiles(path, searchPattern);

Atualização : O LINQ não é uma opção , ele deve ser searchPatterntransferido GetFiles, conforme especificado na pergunta.

Seb Nilsson
fonte
Eu não acho que exista. Liste todos os arquivos e filtre manualmente ou faça uma união em vários pesquisadores. Mas tenho certeza de que já vi essa pergunta exata no SO antes.
CodesInChaos
Perguntado e respondido anteriormente aqui: stackoverflow.com/questions/163162/…
David

Respostas:

41

Eu acredito que não há solução "pronta para uso", essa é uma limitação do método Directory.GetFiles.

É bastante fácil escrever seu próprio método, aqui está um exemplo .

O código pode ser:

/// <summary>
/// Returns file names from given folder that comply to given filters
/// </summary>
/// <param name="SourceFolder">Folder with files to retrieve</param>
/// <param name="Filter">Multiple file filters separated by | character</param>
/// <param name="searchOption">File.IO.SearchOption, 
/// could be AllDirectories or TopDirectoryOnly</param>
/// <returns>Array of FileInfo objects that presents collection of file names that 
/// meet given filter</returns>
public string[] getFiles(string SourceFolder, string Filter, 
 System.IO.SearchOption searchOption)
{
 // ArrayList will hold all file names
ArrayList alFiles = new ArrayList();

 // Create an array of filter string
 string[] MultipleFilters = Filter.Split('|');

 // for each filter find mathing file names
 foreach (string FileFilter in MultipleFilters)
 {
  // add found file names to array list
  alFiles.AddRange(Directory.GetFiles(SourceFolder, FileFilter, searchOption));
 }

 // returns string array of relevant file names
 return (string[])alFiles.ToArray(typeof(string));
}
Daniel B
fonte
7
Essa é uma maneira muito insuficiente de fazer isso, pois você fará um loop em todo o diretório para cada filtro. Em vez disso, você deve verificar cada arquivo se ele tiver o filtro e adicionar à lista. Você pode usar a resposta explicada neste tópico: stackoverflow.com/questions/3754118/…
ot0
190
var filteredFiles = Directory
    .GetFiles(path, "*.*")
    .Where(file => file.ToLower().EndsWith("aspx") || file.ToLower().EndsWith("ascx"))
    .ToList();

Edit 23-07-2014

Você pode fazer isso no .NET 4.5 para uma enumeração mais rápida:

var filteredFiles = Directory
    .EnumerateFiles(path) //<--- .NET 4.5
    .Where(file => file.ToLower().EndsWith("aspx") || file.ToLower().EndsWith("ascx"))
    .ToList();

Directory.EnumerateFiles no MSDN

jgauffin
fonte
5
@ Mario Vernari: GetFilesretorna string[].
jgauffin
4
Você deve remover o * do argumento EndsWith (), ele não faz correspondências curinga.
Hans Passant
3
se comparar extensões do arquivo, ele retornará uma correspondência exata como '.Where (file => new FileInfo (arquivo) .Extension.Equals (". aspx") || novo FileInfo (arquivo) .Extension.Equals (". ascx") ) '
Damith 12/08
3
Não se esqueça do novo .NET4 Directory.EnumerateFilespara um aumento de performance ... stackoverflow.com/questions/5669617/...
drzaus
6
E você sempre pode usar file.EndsWith("...", StringComparison.InvariantCultureIgnoreCase);em vez deToLower
drzaus
30

GetFiles pode corresponder apenas a um único padrão, mas você pode usar o Linq para chamar GetFiles com vários padrões:

FileInfo[] fi = new string[]{"*.txt","*.doc"}
    .SelectMany(i => di.GetFiles(i, SearchOption.AllDirectories))
    .ToArray();

Consulte a seção de comentários aqui: http://www.codeproject.com/KB/aspnet/NET_DirectoryInfo.aspx

Ulrik Magnusson
fonte
2
Eles colidirão se os padrões se sobreporem. Por exemplo new string[]{"*.txt","filename.*"},. No entanto, a chamada para Distinctrealmente não resolve esse problema, pois os objetos FileInfo são comparados usando a igualdade de referência, e não a semântica. Pode ser corrigido removendo Distinctou passando um IEqualityComparer<FileInfo>. Editado para fazer o primeiro.
Brian
Eu acho que SelectManyiterará sobre a mesma estrutura de arquivos novamente (e novamente), para que possa ficar abaixo do ideal em termos de desempenho.
Dejan
28

Eu gosto desse método, porque é legível e evita várias iterações do diretório:

var allowedExtensions = new [] {".doc", ".docx", ".pdf", ".ppt", ".pptx", ".xls", ".xslx"}; 
var files = Directory
    .GetFiles(folder)
    .Where(file => allowedExtensions.Any(file.ToLower().EndsWith))
    .ToList();
Marc
fonte
2
Eu gosto muito mais disso porque não tenho que analisar minha matriz de extensões e adicioná-la ao regex ou outro trabalho manual. Obrigado!
Ian Newland
@Jodrell, ou simplesmente umHashSet<string>
Jodrell
O HashSet <string> em vez de uma matriz para a extensão não faz sentido aqui, pois o número de extensões é limitado e a matriz é iterada para cada arquivo, até que EndsWith () seja verdadeiro. Se o método precisar ser ajustado para desempenho para um número muito grande de extensões, um Hashset poderá ser usado. Para entrar em vigor, a extensão de cada arquivo precisaria ser correspondida explicitamente (dividir e depois corresponder) em vez do método EndsWith () -. Isso prejudicará a legibilidade e não será de uso significativo na maioria, se não em todos os casos de uso da vida real. Por isso, revirei a edição da comunidade.
Marc
15

Temo que você tenha que fazer algo assim, eu mudei o regex daqui .

var searchPattern = new Regex(
    @"$(?<=\.(aspx|ascx))", 
    RegexOptions.IgnoreCase);
var files = Directory.EnumerateFiles(path)
    .Where(f => searchPattern.IsMatch(f))
    .ToList();
Jodrell
fonte
esta parece ser uma boa abordagem, a parte que falta é ter um (de trabalho) expressão regular testado
júnior Mayhé
14
var filteredFiles = Directory
    .EnumerateFiles(path, "*.*") // .NET4 better than `GetFiles`
    .Where(
        // ignorecase faster than tolower...
        file => file.ToLower().EndsWith("aspx")
        || file.EndsWith("ascx", StringComparison.OrdinalIgnoreCase))
    .ToList();

Ou pode ser mais rápido dividir e mesclar seus globs (pelo menos parece mais limpo):

"*.ext1;*.ext2".Split(';')
    .SelectMany(g => Directory.EnumerateFiles(path, g))
    .ToList();
drzaus
fonte
e republicando a pergunta "original" com mais detalhes - stackoverflow.com/questions/163162/…
drzaus
6

A solução fácil de lembrar, preguiçosa e talvez imperfeita:

Directory.GetFiles(dir, "*.dll").Union(Directory.GetFiles(dir, "*.exe"))
Jonathan
fonte
4

Eu usaria o seguinte:

var ext = new string[] { ".ASPX", ".ASCX" };
FileInfo[] collection = (from fi in new DirectoryInfo(path).GetFiles()
                         where ext.Contains(fi.Extension.ToUpper())
                         select fi)
                         .ToArray();

EDIT: corrigida devido incompatibilidade entre Directory e DirectoryInfo

Mario Vernari
fonte
3

Uma maneira mais eficiente de obter arquivos com as extensões ".aspx" e ".ascx" que evita consultar o sistema de arquivos várias vezes e evita o retorno de muitos arquivos indesejados é pré-filtrar os arquivos usando um padrão aproximado de pesquisa e para refinar o resultado posteriormente:

var filteredFiles = Directory.GetFiles(path, "*.as?x")
    .Select(f => f.ToLowerInvariant())
    .Where(f => f.EndsWith("px") || f.EndsWith("cx"))
    .ToList();
Olivier Jacot-Descombes
fonte
2

Eu tentaria especificar algo como

var searchPattern = "as?x";

deve funcionar.

Davide Piras
fonte
Hah! Eu tinha medo de que o aspx e o ascx fossem muito semelhantes e renderizassem uma solução de hack como essa. Eu quero algo geral.
Seb Nilsson
2
    /// <summary>
    /// Returns the names of files in a specified directories that match the specified patterns using LINQ
    /// </summary>
    /// <param name="srcDirs">The directories to seach</param>
    /// <param name="searchPatterns">the list of search patterns</param>
    /// <param name="searchOption"></param>
    /// <returns>The list of files that match the specified pattern</returns>
    public static string[] GetFilesUsingLINQ(string[] srcDirs,
         string[] searchPatterns,
         SearchOption searchOption = SearchOption.AllDirectories)
    {
        var r = from dir in srcDirs
                from searchPattern in searchPatterns
                from f in Directory.GetFiles(dir, searchPattern, searchOption)
                select f;

        return r.ToArray();
    }
A.Ramazani
fonte
2
    public static bool CheckFiles(string pathA, string pathB)
    {
        string[] extantionFormat = new string[] { ".war", ".pkg" };
        return CheckFiles(pathA, pathB, extantionFormat);
    }
    public static bool CheckFiles(string pathA, string pathB, string[] extantionFormat)
    {
        System.IO.DirectoryInfo dir1 = new System.IO.DirectoryInfo(pathA);
        System.IO.DirectoryInfo dir2 = new System.IO.DirectoryInfo(pathB);
        // Take a snapshot of the file system. list1/2 will contain only WAR or PKG 
        // files
        // fileInfosA will contain all of files under path directories 
        FileInfo[] fileInfosA = dir1.GetFiles("*.*", 
                              System.IO.SearchOption.AllDirectories);
        // list will contain all of files that have ..extantion[]  
        // Run on all extantion in extantion array and compare them by lower case to 
        // the file item extantion ...
        List<System.IO.FileInfo> list1 = (from extItem in extantionFormat
                                          from fileItem in fileInfosA
                                          where extItem.ToLower().Equals 
                                          (fileItem.Extension.ToLower())
                                          select fileItem).ToList();
        // Take a snapshot of the file system. list1/2 will contain only WAR or  
        // PKG files
        // fileInfosA will contain all of files under path directories 
        FileInfo[] fileInfosB = dir2.GetFiles("*.*", 
                                       System.IO.SearchOption.AllDirectories);
        // list will contain all of files that have ..extantion[]  
        // Run on all extantion in extantion array and compare them by lower case to 
        // the file item extantion ...
        List<System.IO.FileInfo> list2 = (from extItem in extantionFormat
                                          from fileItem in fileInfosB
                                          where extItem.ToLower().Equals            
                                          (fileItem.Extension.ToLower())
                                          select fileItem).ToList();
        FileCompare myFileCompare = new FileCompare();
        // This query determines whether the two folders contain 
        // identical file lists, based on the custom file comparer 
        // that is defined in the FileCompare class. 
        return list1.SequenceEqual(list2, myFileCompare);
    }
yossi goldberg
fonte
2

Em vez da função EndsWith, eu escolheria usar o Path.GetExtension()método. Aqui está o exemplo completo:

var filteredFiles = Directory.EnumerateFiles( path )
.Where(
    file => Path.GetExtension(file).Equals( ".aspx", StringComparison.OrdinalIgnoreCase ) ||
            Path.GetExtension(file).Equals( ".ascx", StringComparison.OrdinalIgnoreCase ) );

ou:

var filteredFiles = Directory.EnumerateFiles(path)
.Where(
    file => string.Equals( Path.GetExtension(file), ".aspx", StringComparison.OrdinalIgnoreCase ) ||
            string.Equals( Path.GetExtension(file), ".ascx", StringComparison.OrdinalIgnoreCase ) );

(Use StringComparison.OrdinalIgnoreCasese você se preocupa com o desempenho: comparações de strings do MSDN )

Grande chefe
fonte
1

parecido com esta demonstração:

void Main()
{
    foreach(var f in GetFilesToProcess("c:\\", new[] {".xml", ".txt"}))
        Debug.WriteLine(f);
}
private static IEnumerable<string> GetFilesToProcess(string path, IEnumerable<string> extensions)
{
   return Directory.GetFiles(path, "*.*")
       .Where(f => extensions.Contains(Path.GetExtension(f).ToLower()));
}
Gildor
fonte
1
Você tem o Path.GetExtensionque você pode usar.
jgauffin
1

@ Daniel B, obrigado pela sugestão de escrever minha própria versão dessa função. Ele tem o mesmo comportamento do Directory.GetFiles, mas oferece suporte à filtragem de expressões regulares.

string[] FindFiles(FolderBrowserDialog dialog, string pattern)
    {
        Regex regex = new Regex(pattern);

        List<string> files = new List<string>();
        var files=Directory.GetFiles(dialog.SelectedPath);
        for(int i = 0; i < files.Count(); i++)
        {
            bool found = regex.IsMatch(files[i]);
            if(found)
            {
                files.Add(files[i]);
            }
        }

        return files.ToArray();
    }

Achei útil, então pensei em compartilhar.

Artorias2718
fonte
1

versão c # da resposta de @ qfactor77. Esta é a melhor maneira sem o LINQ.

string[] wildcards= {"*.mp4", "*.jpg"};
ReadOnlyCollection<string> filePathCollection = FileSystem.GetFiles(dirPath, Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, wildcards);
string[] filePath=new string[filePathCollection.Count];
filePathCollection.CopyTo(filePath,0);

agora retorne um filePatharray de strings. No começo você precisa

using Microsoft.VisualBasic.FileIO;
using System.Collections.ObjectModel;

Também é necessário adicionar referência a Microsoft.VisualBasic

Rijul Sudhir
fonte
1

Eu fiz uma maneira simples de pesquisar quantas extensões forem necessárias e sem ToLower (), RegEx, foreach ...

List<String> myExtensions = new List<String>() { ".aspx", ".ascx", ".cs" }; // You can add as many extensions as you want.
DirectoryInfo myFolder = new DirectoryInfo(@"C:\FolderFoo");
SearchOption option = SearchOption.TopDirectoryOnly; // Use SearchOption.AllDirectories for seach in all subfolders.
List<FileInfo> myFiles = myFolder.EnumerateFiles("*.*", option)
    .Where(file => myExtensions
    .Any(e => String.Compare(file.Extension, e, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase) == 0))
    .ToList();

Trabalhando no .Net Standard 2.0.

Carlos David López
fonte
1

Você pode fazer assim

new DirectoryInfo(path).GetFiles().Where(Current => Regex.IsMatch(Current.Extension, "\\.(aspx|ascx)", RegexOptions.IgnoreCase)
Gigabyte
fonte
A pergunta é: LINQ não é uma opção, portanto, esta resposta não é útil
Arci
0
var filtered = Directory.GetFiles(path)
    .Where(file => file.EndsWith("aspx", StringComparison.InvariantCultureIgnoreCase) || file.EndsWith("ascx", StringComparison.InvariantCultureIgnoreCase))
    .ToList();
romano
fonte
Adicione explicações adicionais para o código. Isso pode ajudar o OP a entender melhor sua resposta.
user2339071
-2

Só gostaria de dizer que se você usar em FileIO.FileSystem.GetFilesvez deDirectory.GetFiles , permitirá uma variedade de curingas.

Por exemplo:

Dim wildcards As String() = {"*.html", "*.zip"}
Dim ListFiles As List(Of String) = FileIO.FileSystem.GetFiles(directoryyouneed, FileIO.SearchOption.SearchTopLevelOnly, wildcards).ToList
qfactor77
fonte
Onde alguém adquire FileIO?
Joel Martinez
1
Ele já deve estar incluído no seu ambiente no Visual Studio (2015). Faz parte do espaço para nome Microsoft.VisualBasic. No meu caso, é o VisualBasic, porque essa é minha linguagem de escolha.
qfactor77