Можете ли вы вызвать Directory.GetFiles() с несколькими фильтрами?

Я пытаюсь использовать метод Directory.GetFiles() для извлечения списка файлов нескольких типов, таких как mp3 и jpg. Я пробовал оба из них без везения:

Directory.GetFiles("C:\\path", "*.mp3|*.jpg", SearchOption.AllDirectories);
Directory.GetFiles("C:\\path", "*.mp3;*.jpg", SearchOption.AllDirectories);

Есть ли способ сделать это за один вызов?

Ответ 1

Для .NET 4.0 и более поздних версий

var files = Directory.EnumerateFiles("C:\\path", "*.*", SearchOption.AllDirectories)
            .Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));

Для более ранних версий .NET,

var files = Directory.GetFiles("C:\\path", "*.*", SearchOption.AllDirectories)
            .Where(s => s.EndsWith(".mp3") || s.EndsWith(".jpg"));

изменить: Прочитайте комментарии. Улучшение, которое Paul Farry предлагает, и проблема памяти/производительности, которую Christian.K указывает, важно.

Ответ 2

Как насчет этого:

private static string[] GetFiles(string sourceFolder, string filters, System.IO.SearchOption searchOption)
{
   return filters.Split('|').SelectMany(filter => System.IO.Directory.GetFiles(sourceFolder, filter, searchOption)).ToArray();
}

Я нашел его здесь (в комментариях): http://msdn.microsoft.com/en-us/library/wz42302f.aspx

Ответ 3

Если у вас есть большой список расширений для проверки, вы можете использовать следующее. Я не хотел создавать множество операторов OR, поэтому я изменил то, что написал lette.

string supportedExtensions = "*.jpg,*.gif,*.png,*.bmp,*.jpe,*.jpeg,*.wmf,*.emf,*.xbm,*.ico,*.eps,*.tif,*.tiff,*.g01,*.g02,*.g03,*.g04,*.g05,*.g06,*.g07,*.g08";
foreach (string imageFile in Directory.GetFiles(_tempDirectory, "*.*", SearchOption.AllDirectories).Where(s => supportedExtensions.Contains(Path.GetExtension(s).ToLower())))
{
    //do work here
}

Ответ 4

для

var exts = new[] { "mp3", "jpg" };

Вы можете:

public IEnumerable<string> FilterFiles(string path, params string[] exts) {
    return
        Directory
        .EnumerateFiles(path, "*.*")
        .Where(file => exts.Any(x => file.EndsWith(x, StringComparison.OrdinalIgnoreCase)));
}

Но реальная выгода EnumerateFiles появляется, когда вы разделяете фильтры и объединяете результаты:

public IEnumerable<string> FilterFiles(string path, params string[] exts) {
    return 
        exts.Select(x => "*." + x) // turn into globs
        .SelectMany(x => 
            Directory.EnumerateFiles(path, x)
            );
}

Это немного быстрее, если вам не нужно превращать их в глобусы (т.е. exts = new[] {"*.mp3", "*.jpg"} уже).

Оценка производительности на основе следующего теста LinqPad (примечание: Perf просто повторяет делегат 10000 раз) https://gist.github.com/zaus/7454021

(перепродан и расширен из "дубликата", так как этот вопрос специально запросил LINQ: Несколько расширений файлов searchPattern для System.IO.Directory.GetFiles)

Ответ 5

Я знаю старый вопрос, но LINQ: (.NET40 +)

var files = Directory.GetFiles("path_to_files").Where(file => Regex.IsMatch(file, @"^.+\.(wav|mp3|txt)$"));

Ответ 6

Другой способ использования Linq, но без необходимости возвращать все и фильтровать его в памяти.

var files = Directory.GetFiles("C:\\path", "*.mp3", SearchOption.AllDirectories).Union(Directory.GetFiles("C:\\path", "*.jpg", SearchOption.AllDirectories));

На самом деле это 2 вызова GetFiles(), но я думаю, что он соответствует духу вопроса и возвращает их в один перечислимый.

Ответ 7

Неа. Попробуйте следующее:

List<string> _searchPatternList = new List<string>();
    ...
    List<string> fileList = new List<string>();
    foreach ( string ext in _searchPatternList )
    {
        foreach ( string subFile in Directory.GetFiles( folderName, ext  )
        {
            fileList.Add( subFile );
        }
    }

    // Sort alpabetically
    fileList.Sort();

    // Add files to the file browser control    
    foreach ( string fileName in fileList )
    {
        ...;
    }

Взято из: http://blogs.msdn.com/markda/archive/2006/04/20/580075.aspx

Ответ 8

Пусть

var set = new HashSet<string> { ".mp3", ".jpg" };

Тогда

Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
         .Where(f => set.Contains(
             new FileInfo(f).Extension,
             StringComparer.OrdinalIgnoreCase));

или

from file in Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
from ext in set
where String.Equals(ext, new FileInfo(file).Extension, StringComparison.OrdinalIgnoreCase)
select file;

Ответ 9

Существует также решение для спуска, которое, как представляется, не имеет недостатка в памяти или производительности и довольно элегантно:

string[] filters = new[]{"*.jpg", "*.png", "*.gif"};
string[] filePaths = filters.SelectMany(f => Directory.GetFiles(basePath, f)).ToArray();

Ответ 10

Я не могу использовать метод .Where, потому что я программирую в .NET Framework 2.0 (Linq поддерживается только в .NET Framework 3.5+).

Нижеприведенный код не чувствителен к регистру (поэтому .CaB или .CaB тоже будут перечислены).

string[] ext = new string[2] { "*.CAB", "*.MSU" };

foreach (string found in ext)
{
    string[] extracted = Directory.GetFiles("C:\\test", found, System.IO.SearchOption.AllDirectories);

    foreach (string file in extracted)
    {
        Console.WriteLine(file);
    }
}

Ответ 11

Следующая функция выполняет поиск по нескольким шаблонам, разделенным запятыми. Вы также можете указать исключение, например: "! Web.config" будет искать все файлы и исключать "web.config". Шаблоны могут быть смешаны.

private string[] FindFiles(string directory, string filters, SearchOption searchOption)
{
    if (!Directory.Exists(directory)) return new string[] { };

    var include = (from filter in filters.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) where !string.IsNullOrEmpty(filter.Trim()) select filter.Trim());
    var exclude = (from filter in include where filter.Contains(@"!") select filter);

    include = include.Except(exclude);

    if (include.Count() == 0) include = new string[] { "*" };

    var rxfilters = from filter in exclude select string.Format("^{0}$", filter.Replace("!", "").Replace(".", @"\.").Replace("*", ".*").Replace("?", "."));
    Regex regex = new Regex(string.Join("|", rxfilters.ToArray()));

    List<Thread> workers = new List<Thread>();
    List<string> files = new List<string>();

    foreach (string filter in include)
    {
        Thread worker = new Thread(
            new ThreadStart(
                delegate
                {
                    string[] allfiles = Directory.GetFiles(directory, filter, searchOption);
                    if (exclude.Count() > 0)
                    {
                        lock (files)
                            files.AddRange(allfiles.Where(p => !regex.Match(p).Success));
                    }
                    else
                    {
                        lock (files)
                            files.AddRange(allfiles);
                    }
                }
            ));

        workers.Add(worker);

        worker.Start();
    }

    foreach (Thread worker in workers)
    {
        worker.Join();
    }

    return files.ToArray();

}

Использование:

foreach (string file in FindFiles(@"D:\628.2.11", @"!*.config, !*.js", SearchOption.AllDirectories))
            {
                Console.WriteLine(file);
            }

Ответ 12

Просто нашел другой способ сделать это. Все еще не одна операция, а выкидывание, чтобы увидеть, что другие люди думают об этом.

private void getFiles(string path)
{
    foreach (string s in Array.FindAll(Directory.GetFiles(path, "*", SearchOption.AllDirectories), predicate_FileMatch))
    {
        Debug.Print(s);
    }
}

private bool predicate_FileMatch(string fileName)
{
    if (fileName.EndsWith(".mp3"))
        return true;
    if (fileName.EndsWith(".jpg"))
        return true;
    return false;
}

Ответ 13

List<string> FileList = new List<string>();
DirectoryInfo di = new DirectoryInfo("C:\\DirName");

IEnumerable<FileInfo> fileList = di.GetFiles("*.*");

//Create the query
IEnumerable<FileInfo> fileQuery = from file in fileList
                                  where (file.Extension.ToLower() == ".jpg" || file.Extension.ToLower() == ".png")
                                  orderby file.LastWriteTime
                                  select file;

foreach (System.IO.FileInfo fi in fileQuery)
{
    fi.Attributes = FileAttributes.Normal;
    FileList.Add(fi.FullName);
}

Ответ 14

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

Я бы создал функцию, которая сама взяла массив в строках с необходимыми расширениями, а затем перебирала на этом массиве все необходимые вызовы. Эта функция вернет общий список файлов, соответствующих адресам, которые я отправил.

Надеюсь, что это поможет.

Ответ 15

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

Ответ 16

Сделайте расширения, в которых требуется одна строка i.e ".mp3.jpg.wma.wmf", а затем проверьте, содержит ли каждый файл требуемое расширение. Это работает с .net 2.0, поскольку он не использует LINQ.

string myExtensions=".jpg.mp3";

string[] files=System.IO.Directory.GetFiles("C:\myfolder");

foreach(string file in files)
{
   if(myExtensions.ToLower().contains(System.IO.Path.GetExtension(s).ToLower()))
   {
      //this file has passed, do something with this file

   }
}

Преимущество такого подхода заключается в том, что вы можете добавлять или удалять расширения без редактирования кода. Чтобы добавить png-изображения, просто напишите myExtensions = ". jpg.mp3.png".

Ответ 17

DirectoryInfo directory = new DirectoryInfo(Server.MapPath("~/Contents/"));

//Using Union

FileInfo[] files = directory.GetFiles("*.xlsx")
                            .Union(directory
                            .GetFiles("*.csv"))
                            .ToArray();

Ответ 18

в .NET 2.0 (без Linq):

public static List<string> GetFilez(string path, System.IO.SearchOption opt,  params string[] patterns)
{
    List<string> filez = new List<string>();
    foreach (string pattern in patterns)
    {
        filez.AddRange(
            System.IO.Directory.GetFiles(path, pattern, opt)
        );
    }


    // filez.Sort(); // Optional
    return filez; // Optional: .ToArray()
}

Затем используйте его:

foreach (string fn in GetFilez(path
                             , System.IO.SearchOption.AllDirectories
                             , "*.xml", "*.xml.rels", "*.rels"))
{}

Ответ 19

Что насчет

string[] filesPNG = Directory.GetFiles(path, "*.png");
string[] filesJPG = Directory.GetFiles(path, "*.jpg");
string[] filesJPEG = Directory.GetFiles(path, "*.jpeg");

int totalArraySizeAll = filesPNG.Length + filesJPG.Length + filesJPEG.Length;
List<string> filesAll = new List<string>(totalArraySizeAll);
filesAll.AddRange(filesPNG);
filesAll.AddRange(filesJPG);
filesAll.AddRange(filesJPEG);

Ответ 20

У меня была та же проблема и я не мог найти правильное решение, поэтому написал функцию GetFiles:

/// <summary>
/// Get all files with a specific extension
/// </summary>
/// <param name="extensionsToCompare">string list of all the extensions</param>
/// <param name="Location">string of the location</param>
/// <returns>array of all the files with the specific extensions</returns>
public string[] GetFiles(List<string> extensionsToCompare, string Location)
{
    List<string> files = new List<string>();
    foreach (string file in Directory.GetFiles(Location))
    {
        if (extensionsToCompare.Contains(file.Substring(file.IndexOf('.')+1).ToLower())) files.Add(file);
    }
    files.Sort();
    return files.ToArray();
}

Эта функция будет вызывать Directory.Getfiles() только один раз.

Например, вызовите функцию следующим образом:

string[] images = GetFiles(new List<string>{"jpg", "png", "gif"}, "imageFolder");

EDIT: для получения одного файла с несколькими расширениями используйте этот:

/// <summary>
    /// Get the file with a specific name and extension
    /// </summary>
    /// <param name="filename">the name of the file to find</param>
    /// <param name="extensionsToCompare">string list of all the extensions</param>
    /// <param name="Location">string of the location</param>
    /// <returns>file with the requested filename</returns>
    public string GetFile( string filename, List<string> extensionsToCompare, string Location)
    {
        foreach (string file in Directory.GetFiles(Location))
        {
            if (extensionsToCompare.Contains(file.Substring(file.IndexOf('.') + 1).ToLower()) &&& file.Substring(Location.Length + 1, (file.IndexOf('.') - (Location.Length + 1))).ToLower() == filename) 
                return file;
        }
        return "";
    }

Например, вызовите функцию следующим образом:

string image = GetFile("imagename", new List<string>{"jpg", "png", "gif"}, "imageFolder");

Ответ 21

Интересно, почему так много "решений" отправлено?

Если мой новичок-понимание того, как работает GetFiles, прав, есть только два варианта, и любое из вышеперечисленных решений может быть сведено к следующему:

  • GetFiles, а затем фильтр: Быстро, но убийца памяти из-за хранения служебных данных до тех пор, пока фильтры не будут применены.

  • Фильтр в то время как GetFiles: медленнее, чем больше фильтров установлены, а использование низкой памяти, поскольку накладные расходы не хранятся.
    Это объясняется в одном из приведенных выше сообщений с впечатляющим эталоном: каждый параметр фильтра вызывает отдельную GetFile, так что одна и та же часть жесткого диска читается несколько раз.

На мой взгляд, вариант 1) лучше, но использование SearchOption.AllDirectories в таких папках, как C: \, будет использовать огромные объемы памяти.
Поэтому я бы просто сделал рекурсивный под-метод, который проходит через все подпапки, используя опцию 1)

Это должно привести только к 1 GetFiles-операции в каждой папке и, следовательно, быть быстрым (вариант 1), но использовать только небольшой объем памяти, так как фильтры применяются после того, как из каждой вложенной папки удаляются служебные данные каждого подкаталога.

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

Ответ 22

Или вы можете просто преобразовать строку расширений в String ^

vector <string>  extensions = { "*.mp4", "*.avi", "*.flv" };
for (int i = 0; i < extensions.size(); ++i)
{
     String^ ext = gcnew String(extensions[i].c_str());;
     String^ path = "C:\\Users\\Eric\\Videos";
     array<String^>^files = Directory::GetFiles(path,ext);
     Console::WriteLine(ext);
     cout << " " << (files->Length) << endl;
}

Ответ 23

Я не знаю, какое решение лучше, но я использую это:

String[] ext = "*.ext1|*.ext2".Split('|');

            List<String> files = new List<String>();
            foreach (String tmp in ext)
            {
                files.AddRange(Directory.GetFiles(dir, tmp, SearchOption.AllDirectories));
            }