Несколько расширений файлов searchPattern для System.IO.Directory.GetFiles

Каков синтаксис для установки нескольких расширений файлов как searchPattern на Directory.GetFiles()? Например, фильтрация файлов с расширениями .aspx и .ascx.

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

Обновление: LINQ не вариант, он должен быть searchPattern, переданным в GetFiles, как указано в вопросе.


person Seb Nilsson    schedule 12.08.2011    source источник
comment
Я не думаю, что есть. Либо перечислите все файлы, а затем отфильтруйте их вручную, либо выполните объединение для нескольких поисковиков. Но я почти уверен, что уже видел этот точный вопрос о SO раньше.   -  person CodesInChaos    schedule 12.08.2011
comment
stackoverflow.com/questions/3527203/   -  person CodesInChaos    schedule 12.08.2011
comment
Ранее здесь задавался вопрос и был дан ответ: stackoverflow.com/questions/163162/   -  person David    schedule 12.08.2011
comment
Почему LINQ никогда не будет вариантом? Это очень распространенная библиотека в .NET, и ее следует использовать при необходимости.   -  person Mark Entingh    schedule 08.11.2020
comment
@MarkEntingh Для использования LINQ код должен перебирать все файлы на диске и фильтровать их на стороне клиента. Предоставляя строку (или что-то подобное) для чего-то на уровне API ОС, может иметься возможность использовать какой-то интеллектуальный кеш в файловой системе или ОС для повышения производительности.   -  person Seb Nilsson    schedule 10.11.2020


Ответы (19)


Я считаю, что нет готового решения, это ограничение метода Directory.GetFiles.

Однако довольно легко написать свой собственный метод, вот пример .

Код может быть таким:

/// <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));
}
person Daniel B    schedule 12.08.2011

var filteredFiles = Directory
    .GetFiles(path, "*.*")
    .Where(file => file.ToLower().EndsWith("aspx") || file.ToLower().EndsWith("ascx"))
    .ToList();

Изменить 23 июля 2014 г.

Вы можете сделать это в .NET 4.5 для более быстрого перечисления:

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

Directory.EnumerateFiles в MSDN

person jgauffin    schedule 12.08.2011
comment
+1 опередил меня :) а нужен ли двойной кронштейн для двойной трубы? - person naveen; 12.08.2011
comment
Вы уверены, что это правильно? Метод GetFiles возвращает массив объектов FileInfo, а не строк. Как можно применить метод EndsWith? - person Mario Vernari; 12.08.2011
comment
@ Марио Вернари: GetFiles возвращает string[]. - person jgauffin; 12.08.2011
comment
@Jgauffin: Вы правы! Извините, я не совпал с DirectoryInfo. Я тоже поправил свой пост. - person Mario Vernari; 12.08.2011
comment
Вы должны удалить * из аргумента EndsWith (), он не выполняет совпадений с подстановочными знаками. - person Hans Passant; 12.08.2011
comment
при сравнении расширений файла будет возвращено точное совпадение, например '.Where (file = ›new FileInfo (file) .Extension.Equals (.aspx) || new FileInfo (file) .Extension.Equals (.ascx))» - person Damith; 12.08.2011
comment
@Damith Действительно, но я думаю, что он будет тяжелее - person Junior Mayhé; 17.08.2012
comment
Не забудьте новый .NET4 Directory.EnumerateFiles для повышения производительности ... stackoverflow.com/questions/5669617/ - person drzaus; 18.09.2013
comment
И вы всегда можете использовать file.EndsWith("...", StringComparison.InvariantCultureIgnoreCase); вместо ToLower - person drzaus; 18.09.2013
comment
Последний комментарий - я думаю, что объединение нескольких поисковых запросов намного быстрее: "*.ext1;*.ext2".Split(';').SelectMany(g => Directory.GetFiles(path, g)).ToList() - person drzaus; 19.09.2013
comment
@drzaus: Почему бы тебе не добавить это в новый ответ. Я бы поддержал это. - person jgauffin; 08.11.2013
comment
@jgauffin готово, спасибо. i ‹3 балла. stackoverflow.com/a/19933502/1037948 - person drzaus; 12.11.2013
comment
Path.GetExtension () лучше для получения расширения файла, по крайней мере, вы должны добавить точку перед типом расширения, например .aspx - person no.Oby; 20.10.2017

Мне нравится этот метод, потому что он удобочитаем и позволяет избежать многократных итераций каталога:

var allowedExtensions = new [] {".doc", ".docx", ".pdf", ".ppt", ".pptx", ".xls", ".xslx"}; 
var files = Directory
    .GetFiles(folder)
    .Where(file => allowedExtensions.Any(file.ToLower().EndsWith))
    .ToList();
person Marc    schedule 06.05.2015
comment
Мне это нравится намного больше, потому что мне не нужно разбирать свой массив расширений и добавлять его в регулярное выражение или выполнять другую ручную работу. Спасибо! - person Ian Newland; 02.10.2015
comment
@Jodrell, или просто HashSet<string> - person Jodrell; 10.09.2018
comment
HashSet ‹string› вместо массива для расширения здесь не имеет смысла, поскольку количество расширений ограничено, и массив повторяется для каждого файла, пока EndsWith () не станет истинным. Если нужно настроить метод для работы с очень большим количеством расширений, можно использовать Hashset. Чтобы вступить в силу, тогда расширение каждого файла должно быть сопоставлено явно (разделить, затем сопоставить) вместо метода EndsWith (). Это повредит читабельности и не принесет существенной пользы в большинстве, если не во всех реальных случаях использования. Поэтому я откатил правку сообщества. - person Marc; 23.12.2019

GetFiles может соответствовать только одному шаблону, но вы можете использовать Linq для вызова GetFiles с несколькими шаблонами:

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

См. Раздел комментариев здесь: http://www.codeproject.com/KB/aspnet/NET_DirectoryInfo.aspx

person Ulrik Magnusson    schedule 12.08.2011
comment
Они столкнутся, если узоры будут накладываться друг на друга. Например, new string[]{"*.txt","filename.*"}. Однако вызов Distinct на самом деле не решает эту проблему, поскольку объекты FileInfo сравниваются с использованием ссылочного равенства, а не семантического равенства. Это можно исправить, удалив Distinct или передав ему IEqualityComparer<FileInfo>. Отредактировал, чтобы сделать первое. - person Brian; 09.07.2015
comment
Я думаю, что SelectMany будет повторять одну и ту же файловую структуру снова (и снова), поэтому она может быть неоптимальной с точки зрения производительности. - person Dejan; 19.03.2019

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();

Или может быть быстрее разделить и объединить ваши шары (по крайней мере, это выглядит чище):

"*.ext1;*.ext2".Split(';')
    .SelectMany(g => Directory.EnumerateFiles(path, g))
    .ToList();
person drzaus    schedule 12.11.2013
comment
и репост по исходному вопросу с более подробной информацией - stackoverflow.com/questions/163162/ - person drzaus; 13.11.2013


Легкое для запоминания, ленивое и, возможно, несовершенное решение:

Directory.GetFiles(dir, "*.dll").Union(Directory.GetFiles(dir, "*.exe"))
person jonathan    schedule 28.03.2016

Я бы использовал следующее:

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

РЕДАКТИРОВАТЬ: исправлено несоответствие между Directory и DirectoryInfo.

person Mario Vernari    schedule 12.08.2011

Я бы попробовал указать что-то вроде

var searchPattern = "as?x";

он должен работать.

person Davide Piras    schedule 12.08.2011
comment
Ха! Я боялся, что aspx и ascx будут слишком похожи и могут выдать подобное решение для взлома. Я хочу чего-нибудь общего. - person Seb Nilsson; 12.08.2011

Более эффективный способ получения файлов с расширениями «.aspx» и «.ascx», который позволяет избежать многократных запросов к файловой системе и избежать возврата большого количества нежелательных файлов, - это предварительная фильтрация файлов с использованием приблизительного шаблона поиска и чтобы потом уточнить результат:

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

Вместо функции EndsWith я бы предпочел использовать метод Path.GetExtension(). Вот полный пример:

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

or:

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

(Используйте StringComparison.OrdinalIgnoreCase, если вам важна производительность: MSDN сравнения строк)

person BigChief    schedule 12.10.2015

Вы можете сделать это так

new DirectoryInfo(path).GetFiles().Where(Current => Regex.IsMatch(Current.Extension, "\\.(aspx|ascx)", RegexOptions.IgnoreCase)
person Gigabyte    schedule 06.05.2020
comment
В вопросе: LINQ не является вариантом, поэтому этот ответ бесполезен - person Arci; 06.05.2020

выглядит так:

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()));
}
person Gildor    schedule 08.11.2013
comment
У вас есть Path.GetExtension, который вы можете использовать. - person jgauffin; 08.11.2013

@Daniel B, спасибо за предложение написать свою версию этой функции. Он имеет то же поведение, что и Directory.GetFiles, но поддерживает фильтрацию регулярных выражений.

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

Я нашел это полезным, поэтому решил поделиться.

person Artorias2718    schedule 02.07.2016

С # версия ответа @ qfactor77. Это лучший способ без 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);

теперь верните filePath строковый массив. Вначале вам нужно

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

также нужно добавить ссылку на Microsoft.VisualBasic

person Rijul Sudhir    schedule 19.03.2018

Я сделал простой способ найти столько расширений, сколько вам нужно, и без 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();

Работает над .Net Standard 2.0.

person Carlos David López    schedule 14.02.2019

Просто хочу сказать, что если вы используете FileIO.FileSystem.GetFiles вместо Directory.GetFiles, это позволит использовать массив подстановочных знаков.

Например:

Dim wildcards As String() = {"*.html", "*.zip"}
Dim ListFiles As List(Of String) = FileIO.FileSystem.GetFiles(directoryyouneed, FileIO.SearchOption.SearchTopLevelOnly, wildcards).ToList
person qfactor77    schedule 20.03.2017
comment
Где взять FileIO? - person Joel Martinez; 14.06.2017
comment
Он должен быть уже включен в вашу среду в Visual Studio (2015). Это часть пространства имен Microsoft.VisualBasic. В моем случае это VisualBasic, потому что это мой предпочтительный язык. - person qfactor77; 16.06.2017

(Извините, что пишу это как ответ, но у меня пока нет прав писать комментарии.)

Обратите внимание на то, что () из Microsoft.VisualBasic - это просто оболочка для выполнения поиска каждого предоставленного шаблона и объединения результатов. При проверке источника из файла .pbd вы можете видеть из этого фрагмента FileSystem.FindPaths выполняется для каждого шаблона в коллекции:

private static void FindFilesOrDirectories(
  FileSystem.FileOrDirectory FileOrDirectory,
  string directory,
  SearchOption searchType,
  string[] wildcards,
  Collection<string> Results)
{
    // (...)
    string[] strArray = wildcards;
    int index = 0;
    while (index < strArray.Length)
    {
      string wildCard = strArray[index];
      FileSystem.AddToStringCollection(Results, FileSystem.FindPaths(FileOrDirectory, directory, wildCard));
      checked { ++index; }
    }
    // (...)
}
person Remco    schedule 25.05.2021

person    schedule
comment
Добавьте дополнительные пояснения к коду. Это может помочь OP лучше понять ваш ответ. - person user2339071; 09.09.2015