Extensiones de archivo múltiples searchPattern para System.IO.Directory.GetFiles

140

¿Cuál es la sintaxis para configurar múltiples extensiones de archivo como searchPatternactivado Directory.GetFiles()? Por ejemplo, filtrar archivos con extensiones .aspx y .ascx .

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

Actualización : LINQ no es una opción , debe searchPatternpasarse GetFiles, como se especifica en la pregunta.

Seb Nilsson
fuente
No creo que haya ninguno. Enumere todos los archivos y luego filtre manualmente o realice una unión en varios buscadores. Pero estoy bastante seguro de que he visto esta pregunta exacta sobre SO antes.
CodesInChaos
Previamente preguntado y respondido aquí: stackoverflow.com/questions/163162/…
David

Respuestas:

41

Creo que no hay una solución "lista para usar", eso es una limitación del método Directory.GetFiles.

Sin embargo, es bastante fácil escribir su propio método, aquí hay un ejemplo .

El código podría 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
fuente
77
Esta es una forma muy insuficiente de hacerlo, ya que realizará un bucle de directorio completo para cada filtro. En su lugar, debe verificar si cada archivo tiene el filtro y luego agregarlo para hacer la lista. Puede usar la respuesta explicada en este hilo: stackoverflow.com/questions/3754118/…
ot0
191
var filteredFiles = Directory
    .GetFiles(path, "*.*")
    .Where(file => file.ToLower().EndsWith("aspx") || file.ToLower().EndsWith("ascx"))
    .ToList();

Editar 2014-07-23

Puede hacer esto en .NET 4.5 para una enumeración más rápida:

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

Directory.EnumerateFiles en MSDN

jgauffin
fuente
55
@ Mario Vernari: GetFilesvuelve string[].
jgauffin
44
Debe eliminar el * del argumento EndsWith (), no hace coincidencias con comodines.
Hans Passant
3
si compara las extensiones del archivo, devolverá una coincidencia exacta como '.Where (file => new FileInfo (archivo) .Extension.Equals (". aspx") || nuevo FileInfo (archivo) .Extension.Equals (". ascx") ) '
Damith
3
No olvide el nuevo .NET4 Directory.EnumerateFilespara aumentar el rendimiento ... stackoverflow.com/questions/5669617/…
drzaus
66
Y siempre se puede usar en file.EndsWith("...", StringComparison.InvariantCultureIgnoreCase);lugar deToLower
drzaus
30

GetFiles solo puede coincidir con un patrón único, pero puede usar Linq para invocar GetFiles con múltiples patrones:

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

Vea la sección de comentarios aquí: http://www.codeproject.com/KB/aspnet/NET_DirectoryInfo.aspx

Ulrik Magnusson
fuente
2
Chocarán si los patrones se superponen. Ej new string[]{"*.txt","filename.*"}. Sin embargo, la llamada a en Distinctrealidad no resuelve este problema, ya que los objetos FileInfo se comparan usando igualdad de referencia, no igualdad semántica. Se puede solucionar eliminando Distincto pasando un an IEqualityComparer<FileInfo>. Editado para hacer lo primero.
Brian
Creo que SelectManyvolverá a iterar sobre la misma estructura de archivos (y nuevamente), por lo que podría ser subóptimo en términos de rendimiento.
Dejan
28

Me gusta este método, porque es legible y evita múltiples iteraciones del directorio:

var allowedExtensions = new [] {".doc", ".docx", ".pdf", ".ppt", ".pptx", ".xls", ".xslx"}; 
var files = Directory
    .GetFiles(folder)
    .Where(file => allowedExtensions.Any(file.ToLower().EndsWith))
    .ToList();
Bagazo
fuente
2
Me gusta mucho más porque no tengo que analizar mi matriz de extensiones y agregarla a expresiones regulares u otro trabajo manual. ¡Gracias!
Ian Newland
@Jodrell, o simplemente unHashSet<string>
Jodrell
HashSet <string> en lugar de una matriz para la extensión no tiene sentido aquí, ya que el número de extensiones es limitado y la matriz se itera para cada archivo, hasta que EndsWith () sea verdadero. Si el método necesita ser ajustado para el rendimiento de una gran cantidad de extensiones, se podría usar un Hashset. Para que surta efecto, la extensión de cada archivo debería coincidir explícitamente (dividir, luego emparejar) en lugar del método EndsWith (). Esto perjudicará la lectura y no será de gran utilidad en la mayoría, si no en todos los casos de uso de la vida real. Por lo tanto, revertí la edición de la comunidad.
Marc
15

Me temo que tendrá que hacer algo como esto. Muteé la expresión regular desde aquí .

var searchPattern = new Regex(
    @"$(?<=\.(aspx|ascx))", 
    RegexOptions.IgnoreCase);
var files = Directory.EnumerateFiles(path)
    .Where(f => searchPattern.IsMatch(f))
    .ToList();
Jodrell
fuente
este parece ser un buen enfoque, la parte que falta es tener una expresión regular probada (funcional)
Junior 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();

O bien, puede ser más rápido dividir y fusionar tus globos (al menos se ve más limpio):

"*.ext1;*.ext2".Split(';')
    .SelectMany(g => Directory.EnumerateFiles(path, g))
    .ToList();
drzaus
fuente
y volver a colocar en la pregunta "original" con más detalle - stackoverflow.com/questions/163162/...
drzaus
6

La solución fácil de recordar, perezosa y quizás imperfecta:

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

Yo usaría lo siguiente:

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

EDITAR: se corrigió la falta de coincidencia entre Directory y DirectoryInfo

Mario Vernari
fuente
3

Una forma más eficiente de obtener archivos con las extensiones ".aspx" y ".ascx" que evita consultar el sistema de archivos varias veces y evita devolver muchos archivos no deseados, es filtrar previamente los archivos mediante un patrón de búsqueda aproximado y para refinar el resultado después:

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

Intentaría especificar algo como

var searchPattern = "as?x";

deberia de funcionar.

Davide Piras
fuente
Ja! Tenía miedo de que aspx y ascx fueran demasiado similares y representarían una solución de pirateo como esta. Quiero algo general
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
fuente
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
fuente
2

En lugar de la función EndsWith, elegiría usar el Path.GetExtension()método en su lugar. Aquí está el ejemplo completo:

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

o:

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

(Úselo StringComparison.OrdinalIgnoreCasesi le importa el rendimiento: comparaciones de cadenas de MSDN )

Gran jefe
fuente
1

se parece a esta demostración:

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
fuente
1
Tienes Path.GetExtensionlo que puedes usar.
jgauffin
1

@Daniel B, gracias por la sugerencia de escribir mi propia versión de esta función. Tiene el mismo comportamiento que Directory.GetFiles, pero admite el filtrado de expresiones 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();
    }

Lo encontré útil, así que pensé en compartirlo.

Artorias2718
fuente
1

C # versión de la respuesta de @ qfactor77. Esta es la mejor manera sin 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);

ahora devuelve filePathuna matriz de cadenas. Al principio necesitas

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

También necesita agregar referencia a Microsoft.VisualBasic

Rijul Sudhir
fuente
1

Hice una manera simple de buscar tantas extensiones como necesite, y sin 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();

Trabajando en .Net Standard 2.0.

Carlos David López
fuente
1

Puedes hacerlo asi

new DirectoryInfo(path).GetFiles().Where(Current => Regex.IsMatch(Current.Extension, "\\.(aspx|ascx)", RegexOptions.IgnoreCase)
Gigabyte
fuente
La pregunta es: LINQ no es una opción, por lo que esta respuesta no es útil
Arci
0
var filtered = Directory.GetFiles(path)
    .Where(file => file.EndsWith("aspx", StringComparison.InvariantCultureIgnoreCase) || file.EndsWith("ascx", StringComparison.InvariantCultureIgnoreCase))
    .ToList();
romano
fuente
Agregue una explicación adicional para el código. Podría ayudar a OP a comprender mejor su respuesta.
user2339071
-2

Solo me gustaría decir que si usa en FileIO.FileSystem.GetFileslugar de Directory.GetFiles, permitirá una variedad de comodines.

Por ejemplo:

Dim wildcards As String() = {"*.html", "*.zip"}
Dim ListFiles As List(Of String) = FileIO.FileSystem.GetFiles(directoryyouneed, FileIO.SearchOption.SearchTopLevelOnly, wildcards).ToList
qfactor77
fuente
¿Dónde se adquiere uno FileIO?
Joel Martinez
1
Ya debería estar incluido en su entorno en Visual Studio (2015). Es parte del espacio de nombres Microsoft.VisualBasic. En mi caso es VisualBasic porque ese es mi lenguaje de elección.
qfactor77