Mehrere Dateierweiterungen searchPattern für System.IO.Directory.GetFiles

140

Was ist die Syntax für die Einstellung mehrerer Dateierweiterungen wie searchPatternauf Directory.GetFiles()? Zum Beispiel das Herausfiltern von Dateien mit den Erweiterungen .aspx und .ascx .

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

Update : LINQ ist keine Option , es muss searchPatternübergeben werden GetFiles, wie in der Frage angegeben.

Seb Nilsson
quelle
Ich glaube nicht, dass es welche gibt. Entweder alle Dateien auflisten und dann manuell filtern oder eine Vereinigung für mehrere Sucher durchführen. Aber ich bin mir ziemlich sicher, dass ich genau diese Frage auf SO schon einmal gesehen habe.
CodesInChaos
Zuvor hier gefragt und beantwortet: stackoverflow.com/questions/163162/…
David

Antworten:

41

Ich glaube, es gibt keine "out of the box" -Lösung, das ist eine Einschränkung der Directory.GetFiles-Methode.

Es ist jedoch ziemlich einfach, eine eigene Methode zu schreiben. Hier ist ein Beispiel .

Der Code könnte sein:

/// <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));
}
Daniel B.
quelle
7
Dies ist eine sehr unzureichende Methode, da Sie für jeden Filter das gesamte Verzeichnis durchlaufen. Stattdessen sollten Sie für jede Datei prüfen, ob sie den Filter enthält, und dann hinzufügen, um die Liste zu erstellen. Sie können die in diesem Thread erläuterte Antwort verwenden: stackoverflow.com/questions/3754118/…
ot0
190
var filteredFiles = Directory
    .GetFiles(path, "*.*")
    .Where(file => file.ToLower().EndsWith("aspx") || file.ToLower().EndsWith("ascx"))
    .ToList();

Bearbeiten 2014-07-23

Sie können dies in .NET 4.5 für eine schnellere Aufzählung tun:

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

Directory.EnumerateFiles in MSDN

jgauffin
quelle
5
@Mario Vernari: GetFileskehrt zurück string[].
Jgauffin
4
Sie müssen das * aus dem EndsWith () -Argument entfernen, es werden keine Platzhalterübereinstimmungen durchgeführt.
Hans Passant
3
Wenn Sie Dateierweiterungen vergleichen, wird eine genaue Übereinstimmung wie '.Where (file => new FileInfo (Datei) .Extension.Equals (". aspx") || new FileInfo (Datei) .Extension.Equals (". ascx") zurückgegeben. ) '
Damith
3
Vergessen Sie nicht das neue .NET4 Directory.EnumerateFilesfür eine Leistungssteigerung ... stackoverflow.com/questions/5669617/…
drzaus
6
Und Sie können immer file.EndsWith("...", StringComparison.InvariantCultureIgnoreCase);eher alsToLower
drzaus
30

GetFiles kann nur mit einem einzelnen Muster übereinstimmen, aber Sie können Linq verwenden, um GetFiles mit mehreren Mustern aufzurufen:

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

Siehe den Kommentarbereich hier: http://www.codeproject.com/KB/aspnet/NET_DirectoryInfo.aspx

Ulrik Magnusson
quelle
2
Sie kollidieren, wenn sich die Muster überlappen. ZB , new string[]{"*.txt","filename.*"}. Der Aufruf von Distinctlöst dieses Problem jedoch nicht wirklich, da FileInfo-Objekte mit Referenzgleichheit und nicht mit semantischer Gleichheit verglichen werden. Es könnte behoben werden, indem entweder das entfernt Distinctoder es übergeben wird IEqualityComparer<FileInfo>. Bearbeitet, um das erstere zu tun.
Brian
Ich würde denken, dass dies SelectManyimmer wieder über dieselbe Dateistruktur iteriert, so dass es in Bezug auf die Leistung möglicherweise nicht optimal ist.
Dejan
28

Ich mag diese Methode, weil sie lesbar ist und mehrere Iterationen des Verzeichnisses vermeidet:

var allowedExtensions = new [] {".doc", ".docx", ".pdf", ".ppt", ".pptx", ".xls", ".xslx"}; 
var files = Directory
    .GetFiles(folder)
    .Where(file => allowedExtensions.Any(file.ToLower().EndsWith))
    .ToList();
Marc
quelle
2
Ich mag das viel besser, weil ich mein Erweiterungsarray nicht analysieren und es zu Regex oder anderen manuellen Arbeiten hinzufügen muss. Vielen Dank!
Ian Newland
@ HashSet<string>
Jodrell
HashSet <string> anstelle eines Arrays für die Erweiterung macht hier keinen Sinn, da die Anzahl der Erweiterungen begrenzt ist und das Array für jede Datei iteriert wird, bis EndsWith () wahr wird. Wenn die Methode für eine sehr große Anzahl von Erweiterungen auf Leistung optimiert werden muss, kann ein Hashset verwendet werden. Um wirksam zu werden, müsste die Erweiterung jeder Datei anstelle der EndsWith () - Methode explizit abgeglichen (geteilt, dann abgeglichen) werden. Dies beeinträchtigt die Lesbarkeit und ist in den meisten, wenn nicht allen realen Anwendungsfällen von keinem nennenswerten Nutzen. Ich habe dafür die Community-Bearbeitung zurückgesetzt.
Marc
15

Ich fürchte, Sie müssen so etwas tun, ich habe den regulären Ausdruck von hier aus mutiert .

var searchPattern = new Regex(
    @"$(?<=\.(aspx|ascx))", 
    RegexOptions.IgnoreCase);
var files = Directory.EnumerateFiles(path)
    .Where(f => searchPattern.IsMatch(f))
    .ToList();
Jodrell
quelle
Dies scheint ein guter Ansatz zu sein. Der fehlende Teil besteht darin, einen getesteten (funktionierenden) regulären Ausdruck zu haben
Junior Mayhé,
14
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();

Oder es kann schneller sein, Ihre Globs zu teilen und zusammenzuführen (zumindest sieht es sauberer aus):

"*.ext1;*.ext2".Split(';')
    .SelectMany(g => Directory.EnumerateFiles(path, g))
    .ToList();
drzaus
quelle
und Reposting auf "ursprüngliche" Frage mit mehr Details - stackoverflow.com/questions/163162/…
drzaus
6

Die leicht zu merkende, faule und vielleicht unvollkommene Lösung:

Directory.GetFiles(dir, "*.dll").Union(Directory.GetFiles(dir, "*.exe"))
Jonathan
quelle
4

Ich würde folgendes verwenden:

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

BEARBEITEN: Aufgrund von Nichtübereinstimmungen zwischen Directory und DirectoryInfo korrigiert

Mario Vernari
quelle
3

Eine effizientere Methode zum Abrufen von Dateien mit den Erweiterungen ".aspx" und ".ascx", bei der das Abfragen des Dateisystems nicht mehrmals und das Zurückgeben vieler unerwünschter Dateien vermieden wird, besteht darin, die Dateien mithilfe eines ungefähren Suchmusters und vorzufiltern um das Ergebnis anschließend zu verfeinern:

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

Ich würde versuchen, so etwas wie zu spezifizieren

var searchPattern = "as?x";

es sollte funktionieren.

Davide Piras
quelle
Hah! Ich hatte Angst, dass Aspx und Ascx zu ähnlich sind und eine solche Hack-Lösung darstellen würden. Ich möchte etwas Allgemeines.
Seb Nilsson
2
    /// <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();
    }
A. Ramazani
quelle
2
    public static bool CheckFiles(string pathA, string pathB)
    {
        string[] extantionFormat = new string[] { ".war", ".pkg" };
        return CheckFiles(pathA, pathB, extantionFormat);
    }
    public static bool CheckFiles(string pathA, string pathB, string[] extantionFormat)
    {
        System.IO.DirectoryInfo dir1 = new System.IO.DirectoryInfo(pathA);
        System.IO.DirectoryInfo dir2 = new System.IO.DirectoryInfo(pathB);
        // Take a snapshot of the file system. list1/2 will contain only WAR or PKG 
        // files
        // fileInfosA will contain all of files under path directories 
        FileInfo[] fileInfosA = dir1.GetFiles("*.*", 
                              System.IO.SearchOption.AllDirectories);
        // list will contain all of files that have ..extantion[]  
        // Run on all extantion in extantion array and compare them by lower case to 
        // the file item extantion ...
        List<System.IO.FileInfo> list1 = (from extItem in extantionFormat
                                          from fileItem in fileInfosA
                                          where extItem.ToLower().Equals 
                                          (fileItem.Extension.ToLower())
                                          select fileItem).ToList();
        // Take a snapshot of the file system. list1/2 will contain only WAR or  
        // PKG files
        // fileInfosA will contain all of files under path directories 
        FileInfo[] fileInfosB = dir2.GetFiles("*.*", 
                                       System.IO.SearchOption.AllDirectories);
        // list will contain all of files that have ..extantion[]  
        // Run on all extantion in extantion array and compare them by lower case to 
        // the file item extantion ...
        List<System.IO.FileInfo> list2 = (from extItem in extantionFormat
                                          from fileItem in fileInfosB
                                          where extItem.ToLower().Equals            
                                          (fileItem.Extension.ToLower())
                                          select fileItem).ToList();
        FileCompare myFileCompare = new FileCompare();
        // This query determines whether the two folders contain 
        // identical file lists, based on the custom file comparer 
        // that is defined in the FileCompare class. 
        return list1.SequenceEqual(list2, myFileCompare);
    }
Yossi Goldberg
quelle
2

Anstelle der EndsWith-Funktion würde ich Path.GetExtension()stattdessen die Methode verwenden. Hier ist das vollständige Beispiel:

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

oder:

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

(Verwenden StringComparison.OrdinalIgnoreCaseSie diese Option, wenn Sie Wert auf Leistung legen : MSDN-Zeichenfolgenvergleiche )

Großer Chef
quelle
1

sehen aus wie diese Demo:

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()));
}
Gildor
quelle
1
Sie haben Path.GetExtensionwas Sie verwenden können.
Jgauffin
1

@ Daniel B, danke für den Vorschlag, meine eigene Version dieser Funktion zu schreiben. Es hat das gleiche Verhalten wie Directory.GetFiles, unterstützt jedoch die Regex-Filterung.

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

Ich fand es nützlich, also dachte ich, ich würde teilen.

Artorias2718
quelle
1

c # -Version der Antwort von @ qfactor77. Dies ist der beste Weg ohne 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);

filePathGeben Sie jetzt das String-Array zurück. Am Anfang brauchst du

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

Außerdem müssen Sie einen Verweis auf hinzufügen Microsoft.VisualBasic

Rijul Sudhir
quelle
1

Ich habe auf einfache Weise so viele Erweiterungen gesucht, wie Sie benötigen, und ohne 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();

Arbeiten an .Net Standard 2.0.

Carlos David López
quelle
1

Sie können es so machen

new DirectoryInfo(path).GetFiles().Where(Current => Regex.IsMatch(Current.Extension, "\\.(aspx|ascx)", RegexOptions.IgnoreCase)
Gigabyte
quelle
In Frage ist: LINQ ist keine Option, daher ist diese Antwort nicht nützlich
Arci
0
var filtered = Directory.GetFiles(path)
    .Where(file => file.EndsWith("aspx", StringComparison.InvariantCultureIgnoreCase) || file.EndsWith("ascx", StringComparison.InvariantCultureIgnoreCase))
    .ToList();
römisch
quelle
Fügen Sie eine zusätzliche Erklärung für den Code hinzu. Es könnte OP helfen, Ihre Antwort besser zu verstehen.
user2339071
-2

Ich möchte nur sagen, dass bei Verwendung von FileIO.FileSystem.GetFilesanstelle Directory.GetFileseine Reihe von Platzhaltern zulässig ist.

Beispielsweise:

Dim wildcards As String() = {"*.html", "*.zip"}
Dim ListFiles As List(Of String) = FileIO.FileSystem.GetFiles(directoryyouneed, FileIO.SearchOption.SearchTopLevelOnly, wildcards).ToList
qfactor77
quelle
Wo erwirbt man FileIO?
Joel Martinez
1
Es sollte bereits in Visual Studio (2015) in Ihrer Umgebung enthalten sein. Es ist Teil des Microsoft.VisualBasic-Namespace. In meinem Fall ist VisualBasic, weil dies meine bevorzugte Sprache ist.
qfactor77