Wiele rozszerzeń plików searchPattern for System.IO.Directory.GetFiles


140

Jaka jest składnia ustawiania wielu rozszerzeń plików na searchPatternwłączone Directory.GetFiles()? Na przykład odfiltrowywanie plików z rozszerzeniami .aspx i .ascx .

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

Aktualizacja : LINQ nie jest opcją , musi być searchPatternprzekazana do GetFiles, jak określono w pytaniu.


Myślę, że nie ma. Albo wyświetl wszystkie pliki, a następnie przefiltruj ręcznie lub wykonaj połączenie w wielu wyszukiwarkach. Ale jestem prawie pewien, że widziałem już to dokładne pytanie na SO.
CodesInChaos


Wcześniej zadane pytanie i odpowiedź na to pytanie: stackoverflow.com/questions/163162/ ...
David

Odpowiedzi:


41

Uważam, że nie ma rozwiązania gotowego do użycia, jest to ograniczenie metody Directory.GetFiles.

Jednak napisanie własnej metody jest dość łatwe, oto przykład .

Kod mógłby wyglądać następująco:

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

7
Jest to bardzo niewystarczający sposób, ponieważ zapętlisz cały katalog dla każdego filtru. Zamiast tego powinieneś sprawdzić, czy każdy plik ma filtr, a następnie dodaj, aby zrobić listę. Możesz skorzystać z odpowiedzi wyjaśnionej w tym wątku: stackoverflow.com/questions/3754118/…
ot0

190
var filteredFiles = Directory
    .GetFiles(path, "*.*")
    .Where(file => file.ToLower().EndsWith("aspx") || file.ToLower().EndsWith("ascx"))
    .ToList();

Edytuj 2014-07-23

Możesz to zrobić w .NET 4.5, aby przyspieszyć wyliczanie:

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

Directory.EnumerateFiles w MSDN


5
@Mario Vernari: GetFileszwraca string[].
jgauffin

4
Musisz usunąć * z argumentu EndsWith (), nie powoduje on dopasowań wieloznacznych.
Hans Passant

3
jeśli porówna rozszerzenia pliku, zwróci dokładne dopasowanie, takie jak '.Where (file => new FileInfo (file) .Extension.Equals (". aspx") || new FileInfo (file) .Extension.Equals (". ascx") ) '
Damith

3
Nie zapomnij o nowym .NET4 Directory.EnumerateFilesdla zwiększenia wydajności ... stackoverflow.com/questions/5669617/
drzaus

6
I zawsze możesz użyć file.EndsWith("...", StringComparison.InvariantCultureIgnoreCase);zamiastToLower
drzaus

30

GetFiles może dopasować tylko jeden wzorzec, ale możesz użyć Linq do wywołania GetFiles z wieloma wzorcami:

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

Zobacz sekcję komentarzy tutaj: http://www.codeproject.com/KB/aspnet/NET_DirectoryInfo.aspx


2
Będą się zderzać, jeśli wzorce się pokrywają. Np new string[]{"*.txt","filename.*"}. Jednak wywołanie w Distinctrzeczywistości nie rozwiązuje tego problemu, ponieważ obiekty FileInfo są porównywane przy użyciu równości odwołań, a nie równości semantycznej. Można to naprawić, usuwając Distinctlub przekazując plik IEqualityComparer<FileInfo>. Edytowany, aby zrobić pierwszy.
Brian,

Myślę, że SelectManybędzie to powtarzać tę samą strukturę plików (i znowu), więc może to być nieoptymalne pod względem wydajności.
Dejan

28

Podoba mi się ta metoda, ponieważ jest czytelna i pozwala uniknąć wielu iteracji katalogu:

var allowedExtensions = new [] {".doc", ".docx", ".pdf", ".ppt", ".pptx", ".xls", ".xslx"}; 
var files = Directory
    .GetFiles(folder)
    .Where(file => allowedExtensions.Any(file.ToLower().EndsWith))
    .ToList();

2
Podoba mi się to o wiele lepiej, ponieważ nie muszę analizować mojej tablicy rozszerzeń i dodawać jej do wyrażenia regularnego lub innej pracy ręcznej. Dzięki!
Ian Newland

@Jodrell, lub po prostuHashSet<string>
Jodrell

HashSet <string> zamiast tablicy dla rozszerzenia nie ma tutaj sensu, ponieważ liczba rozszerzeń jest ograniczona, a tablica jest iterowana dla każdego pliku, aż EndsWith () stanie się true. Jeśli metoda wymaga dostrojenia pod kątem wydajności dla bardzo dużej liczby rozszerzeń, można użyć skrótu. Aby odniosło skutek, rozszerzenie każdego pliku musiałoby zostać dopasowane jawnie (podziel, a następnie dopasuj) zamiast metody EndsWith () -. Spowoduje to pogorszenie czytelności i nie będzie miało większego zastosowania w większości, jeśli nie we wszystkich, rzeczywistych przypadkach użycia. Dlatego wycofałem edycję społeczności.
Marc

15

Obawiam się, że będziesz musiał coś takiego zrobić, zmutowałem stąd wyrażenie regularne .

var searchPattern = new Regex(
    @"$(?<=\.(aspx|ascx))", 
    RegexOptions.IgnoreCase);
var files = Directory.EnumerateFiles(path)
    .Where(f => searchPattern.IsMatch(f))
    .ToList();

wydaje się, że jest to fajne podejście, brakującą częścią jest posiadanie przetestowanego (działającego) wyrażenia regularnego
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();

Lub może być szybsze dzielenie i scalanie twoich globów (przynajmniej wygląda czysto):

"*.ext1;*.ext2".Split(';')
    .SelectMany(g => Directory.EnumerateFiles(path, g))
    .ToList();

i ponowne opublikowanie na „oryginalnym” pytaniu z większą ilością szczegółów - stackoverflow.com/questions/163162/ ...
drzaus

6

Rozwiązanie łatwe do zapamiętania, leniwe i być może niedoskonałe:

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

4

Użyłbym następujących:

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

EDYCJA: poprawiono z powodu niezgodności między Directory i DirectoryInfo


3

Bardziej wydajnym sposobem uzyskiwania plików z rozszerzeniami „.aspx” i „.ascx”, który pozwala uniknąć wielokrotnego sprawdzania systemu plików i zwracania wielu niepożądanych plików, jest wstępne filtrowanie plików przy użyciu przybliżonego wzorca wyszukiwania i aby później doprecyzować wynik:

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

2

Spróbowałbym określić coś takiego

var searchPattern = "as?x";

powinno działać.


Ha! Obawiałem się, że aspx i ascx są zbyt podobne i wyrenderują takie rozwiązanie hakerskie. Chcę czegoś ogólnego.
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();
    }

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

2

Zamiast funkcji EndsWith wybrałbym Path.GetExtension()zamiast tego metodę. Oto pełny przykład:

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

lub:

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

(Użyj, StringComparison.OrdinalIgnoreCasejeśli zależy Ci na wydajności: porównania ciągów MSDN )


1

wygląda jak to 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()));
}

1
Masz to, Path.GetExtensionczego możesz użyć.
jgauffin

1

@Daniel B, dziękuję za sugestię napisania własnej wersji tej funkcji. Zachowuje się tak samo jak Directory.GetFiles, ale obsługuje filtrowanie wyrażeń regularnych.

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

Uznałem to za przydatne, więc pomyślałem, że się nim podzielę


1

wersja c # odpowiedzi @ qfactor77. To najlepszy sposób bez 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);

teraz zwraca filePathtablicę ciągów. Na początku potrzebujesz

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

musisz również dodać odniesienie do Microsoft.VisualBasic


1

Zrobiłem prosty sposób, aby wyszukać tyle rozszerzeń, ile potrzebujesz, i bez 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();

Praca na .Net Standard 2.0.


1

Możesz to zrobić w ten sposób

new DirectoryInfo(path).GetFiles().Where(Current => Regex.IsMatch(Current.Extension, "\\.(aspx|ascx)", RegexOptions.IgnoreCase)

Pytanie brzmi: LINQ nie jest opcją, więc ta odpowiedź nie jest przydatna
Arci

0
var filtered = Directory.GetFiles(path)
    .Where(file => file.EndsWith("aspx", StringComparison.InvariantCultureIgnoreCase) || file.EndsWith("ascx", StringComparison.InvariantCultureIgnoreCase))
    .ToList();

Dodaj dodatkowe wyjaśnienie kodu. Może to pomóc OP lepiej zrozumieć Twoją odpowiedź.
user2339071

-2

Chciałbym tylko powiedzieć, że jeśli używasz FileIO.FileSystem.GetFileszamiastDirectory.GetFiles , pozwoli to na tablicę symboli wieloznacznych.

Na przykład:

Dim wildcards As String() = {"*.html", "*.zip"}
Dim ListFiles As List(Of String) = FileIO.FileSystem.GetFiles(directoryyouneed, FileIO.SearchOption.SearchTopLevelOnly, wildcards).ToList

Skąd się bierze FileIO?
Joel Martinez

1
Powinien być już uwzględniony w Twoim środowisku w programie Visual Studio (2015). Jest częścią przestrzeni nazw Microsoft.VisualBasic. W moim przypadku jest to VisualBasic, ponieważ to mój język z wyboru.
qfactor77
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.