tags:

views:

260

answers:

2

I need to perform some operations on all folders within a file share which match a defined pattern. The pattern may refer to a number of levels in the tree, e.g. \Test\[a-z]+\Project[0-9]{3}

What is the most efficient way to traverse the tree to find all matching folders? Is there a better way to do this than a simple recursive depth first search using DirectoryInfo and di.GetDirectories(), like such:

private void TraverseSubFolder(DirectoryInfo folder)
{
    if (filter.IsMatch(folder.FullName)) {
       DoStuff(folder);
    }

    DirectoryInfo[] subFolders = folder.GetDirectories();
    foreach (DirectoryInfo sf in subFolders)
    {
        TraverseSubFolder(sf);
    }
}
A: 

Directory.GetDirectories(..,.., SearchOption.AllDirectories)

Nestor
That won't work, he's trying to pattern match on the full path
Russell Steen
Does the search pattern parameter support a pattern with multiple levels of folders (like the pattern in my question)? It doesn't appear to support regex patterns, for example, and I couldn't find any good examples on the net beyond *.* etc..
Dexter
the files returned will be full pathed. So you can apply your regular expression on them.
Nestor
+2  A: 

You could use Linq to filter

Regex regex = new Regex("your regex");
var directories = Directory.GetDirectories("c:\\", null, SearchOption.AllDirectories).Where(directory => regex.IsMatch(directory));

The drawback of this approach is that it will still search into unwanted folder that were filtered out since the Where occurs after all folders are returned.

This could be adapted.

Edit

This will not work with SearchOption.AllDirectories since as soon as you hit a folder where you have no right, UnauthorizedAccessException will be thrown.

I don't think you can go without a recursive function because of the check for UnauthorizedAccessException.

I coded this approach using Linq, but it is not very different from your own approach. At least it check for permission. It is still prone to a StackOverflowException.

private static void Traverse(List<string> folders, string rootFolder, Regex filter)
{
    try
    {
        // Test for UnauthorizedAccessException
        new FileIOPermission(FileIOPermissionAccess.PathDiscovery, rootFolder).Demand();

        Array.ForEach(Directory.GetDirectories(rootFolder),
            (directory) =>
            {
                if (filter.IsMatch(directory))
                {
                    folders.Add(directory);

                    Traverse(folders, directory, filter);
                }
            });
    }
    catch 
    {
        // Ignore folder that we don't have access to
    }
}

// Usage example
List<string> folders = new List<string>();
Regex regex = new Regex("^.+$");
Traverse(folders, "e:\\projects", regex);
Pierre-Alain Vigeant
This actually appears to be a bit slower than my recursive search (although it's definitely more elegant). Is there any way to handle unauthorized access exceptions?
Dexter
Yeah it will be slower because it will traverse where it shouldn't go since the filter occurs after the whole traverse. As for access exception, I though GetDirectories was handling that. Weird. I guess that you could check the GetDirectories with Reflector.
Pierre-Alain Vigeant
Thanks for the edit - this is pretty handy info. I ran a few quick tests on a tree of ~5500 folders (couldn't try more because of the unauthorized access exception), and the timings were 2.10 seconds for my recursive DirectoryInfo calls, 3.50 for your original GetDirectories procedure with LINQ filter and 4.47 for @Nestor's method with subsequent regex pattern match. My recursive method still steps into folders which won't match because children might match even if their parent doesn't, so that doesn't explain the time diff to me..
Dexter