Можете ли вы вызвать Directory.GetFiles() с несколькими фильтрами?
Я пытаюсь использовать Directory.GetFiles()
метод для получения списка файлов нескольких типов, таких как mp3
и jpg
"S. Я попробовал оба из следующих без удачи:
Directory.GetFiles("C:\\path", "*.mp3|*.jpg", SearchOption.AllDirectories);
Directory.GetFiles("C:\\path", "*.mp3;*.jpg", SearchOption.AllDirectories);
Есть ли способ сделать это за один звонок?
29 ответов
Для.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, оба очень важны.
Как насчет этого:
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
Если у вас есть большой список расширений для проверки, вы можете использовать следующее. Я не хотел создавать много операторов 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
}
За
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)));
}
- Не забывайте новый.NET4
Directory.EnumerateFiles
для повышения производительности (В чем разница между Directory.EnumerateFiles и Directory.GetFiles?) - "IgnoreCase" должен быть быстрее, чем "ToLower" (
.EndsWith("aspx", StringComparison.OrdinalIgnoreCase)
скорее, чем.ToLower().EndsWith("aspx")
)
Но реальная выгода 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)
Я знаю, что это старый вопрос, но LINQ: (.NET40+)
var files = Directory.GetFiles("path_to_files").Where(file => Regex.IsMatch(file, @"^.+\.(wav|mp3|txt)$"));
Существует также спускаемое решение, которое, похоже, не имеет никаких затрат памяти или производительности и выглядит довольно элегантно:
string[] filters = new[]{"*.jpg", "*.png", "*.gif"};
string[] filePaths = filters.SelectMany(f => Directory.GetFiles(basePath, f)).ToArray();
Другой способ использовать Linq, но без необходимости возвращать все и фильтровать это в памяти.
var files = Directory.GetFiles("C:\\path", "*.mp3", SearchOption.AllDirectories).Union(Directory.GetFiles("C:\\path", "*.jpg", SearchOption.AllDirectories));
На самом деле это 2 звонка GetFiles()
, но я думаю, что это согласуется с духом вопроса и возвращает их в одно перечисляемое.
Позволять
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;
Нету. Попробуйте следующее:
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
Я не могу использовать .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);
}
}
В.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"))
{}
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);
}
DirectoryInfo directory = new DirectoryInfo(Server.MapPath("~/Contents/"));
//Using Union
FileInfo[] files = directory.GetFiles("*.xlsx")
.Union(directory
.GetFiles("*.csv"))
.ToArray();
Если вы используете 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()
делает.
Следующая функция выполняет поиск по нескольким шаблонам, разделенным запятыми. Вы также можете указать исключение, например: "! 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);
}
Как насчет
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);
Интересно, почему опубликовано так много "решений"?
Если мое понимание новичка о том, как работает GetFiles, верно, есть только два варианта, и любое из приведенных выше решений может быть сведено к следующему:
GetFiles, затем фильтр: быстро, но убийца памяти из-за хранения служебных данных до применения фильтров
Фильтровать, пока GetFiles: чем медленнее установлены фильтры, тем меньше использование памяти, поскольку не сохраняется никаких служебных данных.
Это объясняется в одном из приведенных выше постов с впечатляющим тестом: каждый параметр фильтра вызывает отдельную операцию GetFile, поэтому одна и та же часть жесткого диска читается несколько раз.
На мой взгляд, вариант 1) лучше, но использование SearchOption.AllDirectories для таких папок, как C:\, потребовало бы огромных объемов памяти.
Поэтому я бы просто сделал рекурсивный под-метод, который просматривает все подпапки, используя опцию 1).
Это должно вызывать только 1 операцию GetFiles в каждой папке и, следовательно, быть быстрым (Вариант 1), но использовать только небольшой объем памяти, поскольку фильтры применяются после чтения каждой подпапки -> служебная информация удаляется после каждой подпапки.
Пожалуйста, поправьте меня, если я ошибаюсь. Я, как я уже сказал, довольно новичок в программировании, но хочу получить более глубокое понимание вещей, чтобы в конечном итоге стать хорошим в этом:)
Просто нашел другой способ сделать это. Все еще не одна операция, но выбрасывание, чтобы посмотреть, что другие люди думают об этом.
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;
}
Вот простой и элегантный способ получить отфильтрованные файлы
var allowedFileExtensions = ".csv,.txt";
var files = Directory.EnumerateFiles(@"C:\MyFolder", "*.*", SearchOption.TopDirectoryOnly)
.Where(s => allowedFileExtensions.IndexOf(Path.GetExtension(s)) > -1).ToArray();
/// <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();
}
У меня была та же проблема, и я не мог найти правильное решение, поэтому я написал функцию под названием 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");
Использование шаблона поиска GetFiles для фильтрации расширения небезопасно!! Например, у вас есть два файла Test1.xls и Test2.xlsx, и вы хотите отфильтровать файл xls, используя шаблон поиска *.xls, но GetFiles возвращает оба Test1.xls и Test2.xlsx. Я не знал об этом и получил ошибку в работе среда, когда некоторые временные файлы внезапно обрабатывались как правильные файлы. Шаблон поиска был *.txt, а временные файлы были названы *.txt20181028_100753898. Таким образом, шаблон поиска нельзя доверять, необходимо также добавить дополнительную проверку имен файлов.
Сделайте для расширений нужную вам строку, например ".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".
Нет... Я считаю, что вам нужно сделать столько звонков, сколько вам нужно.
Я сам создал бы функцию, получающую массив из строк с нужными мне расширениями, а затем выполнял бы итерацию по этому массиву, делая все необходимые вызовы. Эта функция будет возвращать общий список файлов, соответствующих отправленным мною расширениям.
Надеюсь, поможет.
Я не знаю, какое решение лучше, но я использую это:
String[] ext = "*.ext1|*.ext2".Split('|');
List<String> files = new List<String>();
foreach (String tmp in ext)
{
files.AddRange(Directory.GetFiles(dir, tmp, SearchOption.AllDirectories));
}
Или вы можете просто преобразовать строку расширений в строку ^
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;
}
вы можете добавить это в свой проект
public static class Collectables {
public static List<System.IO.FileInfo> FilesViaPattern(this System.IO.DirectoryInfo fldr, string pattern) {
var filter = pattern.Split(" ");
return fldr.GetFiles( "*.*", System.IO.SearchOption.AllDirectories)
.Where(l => filter.Any(k => l.Name.EndsWith(k))).ToList();
}
}
затем используйте его где угодно, как это
new System.IO.DirectoryInfo("c:\\test").FilesViaPattern("txt doc any.extension");
Это самое короткое и лучшее решение с точки зрения производительности:
foreach (var FilePath in new[] { "*.fbx", "*.obj", "*.mtl" }.SelectMany(X => Directory.EnumerateFiles(DirectoryPath, X))) DoSomething(FilePath);
Надеюсь, это поможет кому-то
//getting only playable Audio/Video Files from open dialog
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = ""All Media Files|*.wav;*.aac;*.wma;*.wmv;*.avi;*.mpg;*.mpeg;*.m1v;*.mp2;*.mp3;*.mpa;*.mpe;*.m3u;*.mp4;*.mov;*.3g2;*.3gp2;*.3gp;*.3gpp;*.m4a;*.cda;*.aif;*.aifc;*.aiff;*.mid;*.midi;*.rmi;*.mkv;*.WAV;*.AAC;*.WMA;*.WMV;*.AVI;*.MPG;*.MPEG;*.M1V;*.MP2;*.MP3;*.MPA;*.MPE;*.M3U;*.MP4;*.MOV;*.3G2;*.3GP2;*.3GP;*.3GPP;*.M4A;*.CDA;*.AIF;*.AIFC;*.AIFF;*.MID;*.MIDI;*.RMI;*.MKV";
dlg.ShowDialog();