it-roy-ru.com

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

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

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

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

321
Jason Z

Для .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"));

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

473
Christoffer Lette

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

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

52
Albert

Если у вас есть большой список расширений для проверки, вы можете использовать следующее. Я не хотел создавать много 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
}
29
jnoreiga

for

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 )

27
drzaus

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

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

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

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

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

11
Dave Rael

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

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

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

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

7
NotMe

Позволять

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;
6
abatishchev

Я не могу использовать метод .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);
    }
}
5
jaysponsored

Следующая функция выполняет поиск по нескольким шаблонам, разделенным запятыми. Вы также можете указать исключение, например: "! 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);
            }
4
Alexander Popov
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);
}
4
Rajeesh Kuthuparakkal
DirectoryInfo directory = new DirectoryInfo(Server.MapPath("~/Contents/"));

//Using Union

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

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

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;
}
3
Jason Z

Как насчет

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);
3
MattyMerrix
/// <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();
}
2
A.Ramazani

Нет ... Я считаю, что вам нужно сделать столько звонков, сколько вам нужно.

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

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

2
sebagomez

Создайте нужные вам расширения в одну строку, т.е. ".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".

2
Evado

в .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"))
{}
2
Stefan Steiger

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

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

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

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

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

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

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

1
Janis

У меня была та же проблема, и я не мог найти правильное решение, поэтому я написал функцию под названием 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");

Правка: Чтобы получить один файл с несколькими расширениями, используйте этот:

/// <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");
1
Quispie

Если вы используете VB.NET (или импортировали зависимость в ваш проект C #), на самом деле существует удобный метод, который позволяет фильтровать по нескольким расширениям:

Microsoft.VisualBasic.FileIO.FileSystem.GetFiles("C:\\path", Microsoft.VisualBasic.FileIO.SearchOption.SearchAllSubDirectories, new string[] {"*.mp3", "*.jpg"});

В VB.NET это может быть доступно через пространство имен My:

My.Computer.FileSystem.GetFiles("C:\path", FileIO.SearchOption.SearchAllSubDirectories, {"*.mp3", "*.jpg"})

К сожалению, эти удобные методы не поддерживают лениво оцененный вариант, как Directory.EnumerateFiles().

0
Crusha K. Rool

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

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

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

Использование шаблона поиска GetFiles для фильтрации расширения небезопасно !! Например, у вас есть два файла Test1.xls и Test2.xlsx, и вы хотите отфильтровать файл xls, используя шаблон поиска * .xls, но GetFiles возвращает оба Test1.xls и Test2.xlsx. Я не знал об этом и получил ошибку в работе среда, когда некоторые временные файлы внезапно обрабатывались как правильные файлы. Шаблон поиска был * .txt, а временные файлы были названы * .txt20181028_100753898. Таким образом, шаблон поиска нельзя доверять, необходимо также добавить дополнительную проверку имен файлов.

0
WillyS