0

I have been browsing all over and I can't seem to find any answers that relate to me specifically.

I'm trying to either return an array of files or count files in a folder based off multiple conditions.

Something similar to this: how search file with multiple criteria of extension of file

However, I'm not looking to search based of file extension only but also file names.

For example:

If I have the following files in a folder:

test1.mp3

test2.avi

test1.jpg


How can I search for all files containing "1". The trick is that users can dynamically add conditions to check for in the file name. So it can't be something like this: dInfo.GetFilesByExtensions(".jpg",".exe",".gif");

The conditions will be added to an array of conditions.

So if the user now also searches for files containing "1" and "t". I would like it to return either "2" (so as a count) or as an array of these files for example, x being the code.

string[] files = x

after the code executes, the files array will look like this:

files[0] = "C:\test1.mp3"; files[1] = "C:\test1.jpg";

Community
  • 1
  • 1
Faker
  • 13
  • 1
  • Look here https://msdn.microsoft.com/en-us/library/dd383458(v=vs.110).aspx at the example at the bottom for a linq query that shows how to search for files containing the word 'europe'. – failedprogramming Apr 08 '15 at 21:43
  • Hmm okay but would require me to do something like this: string[] conditions= { "a", "b" }; foreach (string val in conditions) { var files = from file in Directory.EnumerateFiles(@"C:\library\") where file.ToLower().Contains(val) select file; foreach (var file in files) { Console.WriteLine("{0}", file); } } Where I would be looping through my conditions and again searching through the folder. I think this could work but it seems as if it would be a bit slower. – Faker Apr 08 '15 at 21:50
  • One potential problem with Directory.EnumerateFiles is that you will be searching the whole file path, rather than just the file name. So if you want all files with `'t'` in them, and the file path contains a `'t'`, then it will return all the files... – Rufus L Apr 08 '15 at 21:53
  • @Faker Get something that _works_ first then work on making it _better_. – D Stanley Apr 08 '15 at 21:54
  • @RufusL that is not true. GetFiles by default also retrieves the paths. In your answer you are explicitly doing the search on the file.Name, which is also possible with EnumerateFiles. The difference is actually that with GetFiles, you must wait for the all of the filenames to be returned before you can access the array. – failedprogramming Apr 08 '15 at 21:58
  • @Rufus L thanks is there anyway to substring to "remove" the full folder path minus the file name. I know this is a method and not a string but anything in mind? Thanks for the heads up. @D Stanley I completely agree with that. For the most part I do try improve on code where I can but this has just got me running in circles. – Faker Apr 08 '15 at 22:00
  • @Faker With EnumerateFiles, you can change that query sample on MSDN to a method syntax, then programmatically chain .Where(...) on the query for each extra condition. – failedprogramming Apr 08 '15 at 22:00
  • @failedprogramming First of all, what is not true? Secondly, I'm calling `GetFiles` on a `DirectoryInfo` object (which returns `FileInfo` objects not strings), not a `Directory` object. – Rufus L Apr 08 '15 at 22:03
  • @Faker check my answer below, you can use the `Name` property of the `FileInfo` object. – Rufus L Apr 08 '15 at 22:04
  • @RufusL you were calling a Directory.GetFiles(String) before your edit. I'm not looking to start a flame war with you, so please don't start. – failedprogramming Apr 08 '15 at 22:06
  • @failedprogramming No he wasn't. In his code `directory` was an object, not a namespace. – Setsu Apr 08 '15 at 22:10
  • @Faker Updated answer to also handle a list of separate conditions... – Rufus L Apr 08 '15 at 22:11
  • @RufusL Sorry you are right. I misread the directory for Directory! – failedprogramming Apr 08 '15 at 22:13

3 Answers3

1

You could do some version of the following:

var folderToSearch = "d:\\public";
var nameContains = "1";

var filesMeetingCriteria = new DirectoryInfo(folderToSearch)
    .GetFiles()
    .Where(file => file.Name.Contains(nameContains));

Or, to use a list of conditions, where the file name has to contain all the conditions (but not in any specific order):

var folderToSearch = "d:\\public";
var nameConditions = new List<string> {"r", "t"};

var filesMeetingCriteria =
    new DirectoryInfo(folderToSearch)
        .GetFiles()
        .Where(file =>
            nameConditions.All(condition =>
                file.Name.IndexOf(condition) > -1))
        .ToList();

// To verify the results:
filesMeetingCriteria.ForEach(file => Console.WriteLine(file.Name));

And you can do case-insensitive comparisons using:

var filesMeetingCriteria =
    new DirectoryInfo(folderToSearch)
        .GetFiles()
        .Where(file =>
            nameConditions.All(condition =>
                file.Name.IndexOf(condition,
                    StringComparison.OrdinalIgnoreCase) > -1))
        .ToList();
Rufus L
  • 36,127
  • 5
  • 30
  • 43
0

There is no magic way to do this. The best thing I can think of is have specific filters for the search criteria and use linq to filter.

First get a list of all files in a folder var files = Directory.GetFiles(folder); That also has an extension method where you can wildcard search like var files = Directory.GetFiles(folder, "*.exe"); Now with your filters in mind, this can be an enum for a specific action and a search string. Specific actions like; contains, starts with, ends with...

var files = Directory.GetFiles(folder).Select(Path.GetFileName); //returns only file names
foreach (var filter in filters) 
{
    switch (filter.Action)
    {
        case "contains":
            files = files.Where(f => f.Contains(filter.SearchString));
            break;
        case "next action":
            //filter here
            break;
    }
}

If you want to simplify it in a more magical way, you can use something like var files = Directory.GetFiles(folder, "begin*middle*end*.ex*"); But for something more accurate, this will not work, use the long winded previous mentioned.

Jacob Roberts
  • 1,725
  • 3
  • 16
  • 24
  • `GetFiles` returns the full path, so a call to `System.IO.Path.GetFilename()` is needed (using `select` to project the results should work fine). As for your magical way, keep in mind that `searchPattern` also searches 8.3 filenames which may return surprising results. – Setsu Apr 08 '15 at 22:07
0

You could define an extension method, like this:

public static IEnumerable<string> SearchByName(this DirectoryInfo dir, List<string> keywords)
{
    foreach (FileInfo file in dir.EnumerateFiles())
    {
        string fileName = Path.GetFileNameWithoutExtension(file.Name);

        if (keywords.All(keyword => fileName.Contains(keyword)))
        {
            yield return file.FullName;
        }
    }
}

Here's the query expression equivalent:

var dir = new DirectoryInfo(@"E:\folder");
var keywords = new List<string> { "test", "5" };

var query = from fileInfo in dir.EnumerateFiles()
            let fileName = Path.GetFileNameWithoutExtension(fileInfo.Name)
            where keywords.All(keyword => fileName.Contains(keyword))
            select fileInfo;

foreach (var fileInfo in query)
{
    Console.WriteLine(fileInfo.FullName);
}
chomba
  • 1,390
  • 11
  • 13