web-dev-qa-db-ja.com

複数のフィルタを使ってDirectory.GetFiles()を呼び出すことができますか?

mp3jpgなど、複数の種類のファイルのリストを取得するためにDirectory.GetFiles()メソッドを使用しようとしています。私は次のことを試してみました。

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

1回の呼び出しでこれを実行する方法はありますか?

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

編集:コメントを読んでください。 Paul Farry が示唆している改善、および Christian.K が指摘しているメモリ/パフォーマンスの問題は、どちらも非常に重要です。重要です。

473

これはどう:

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

あなたがそれらをglobに変換する必要がないなら(つまりexts = new[] {"*.mp3", "*.jpg"}が既に)、少し速くなります。

次のLinqPadテストに基づくパフォーマンス評価(注:Perfはデリゲートを10000回繰り返す) https://Gist.github.com/zaus/7454021

(この質問では特にLINQが要求されていないため、 'duplicate'から再投稿および拡張しました: System.IO.Directory.GetFiles の複数ファイル拡張子searchPattern)

27
drzaus

私はそれが古い質問を知っているがLINQ:(.NET40 +)

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

Linqを使用するもう1つの方法。ただし、すべてを返してメモリ内でそれをフィルタリングする必要はありません。

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

それは実際には2つのGetFiles()の呼び出しですが、私はそれが質問の精神と一致していて、それらを1つの列挙型で返すと思います。

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

.NET Framework 2.0でプログラミングしているので.Whereメソッドを使用できません(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);
}
DirectoryInfo directory = new DirectoryInfo(Server.MapPath("~/Contents/"));

//Using Union

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

別の方法を見つけただけです。それでも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;
}
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」という1つの文字列にしてから、各ファイルに必要な拡張子が含まれているかどうかを確認します。 LINQを使用しないので、これが.net 2.0で動作します。

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がどのように機能するかについての私の初心者の理解が正しいのであれば、2つの選択肢しかなく、上記の解決策のうちのどれでもこれらに落とすことができます。

  1. GetFiles、そしてfilter:fast、フィルタが適用されるまでオーバーヘッドを格納するためメモリキラー

  2. GetFiles中のフィルタ:設定されるフィルタは遅くなりますが、オーバーヘッドが格納されないためメモリ使用量が少なくなります。
    これは印象的なベンチマークで上記の記事の1つで説明されています:各フィルタオプションは別々のGetFileオペレーションを引き起こすので、ハードドライブの同じ部分は数回読まれます

私の意見では、オプション1)の方が優れていますが、C:\のようなフォルダにSearchOption.AllDirectoriesを使用すると、大量のメモリが使用されることになります。
そのため、オプション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");

編集:複数の拡張子を持つ1つのファイルを取得するにはこれを使用してください:

/// <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-namespaceを通してアクセスできます。

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という2つのファイルがあり、検索パターン* .xlsを使用してxlsファイルを除外したいのですが、GetFilesがTest1.xlsとTest2.xlsxの両方を返しています。一時ファイルが突然正しいファイルとして扱われたときの環境検索パターンは* .txt、一時ファイルは* .txt20181028_100753898でした。そのため、検索パターンは信頼できないため、ファイル名にもチェックを追加する必要があります。

0
WillyS