2
votes

In this method if i'm not mistaken it's searching for inside the files. So when I typed in searchTerm for example "Form1" it found 46 files.

But now I want to change the method without the searchTerm so it will loop over all the files but in the end I want to get List of all the files types. If there are the same dupilcate dont add them to the List so in the end I will get a List with files types items like: cs,txt,xml so I will know what files types there are.

IEnumerable<string> SearchAccessibleFiles(string root, string searchTerm)
        {
            var files = new List<string>();

            foreach (var file in Directory.EnumerateFiles(root).Where(m => m.Contains(searchTerm)))
            {
                files.Add(file);
            }
            foreach (var subDir in Directory.EnumerateDirectories(root))
            {
                try
                {
                    files.AddRange(SearchAccessibleFiles(subDir, searchTerm));
                }
                catch (UnauthorizedAccessException ex)
                {
                    // ...
                }
            }

            return files;
        }

The problem is that if I'm just making GetFiles and the root directory is c:\ then it will stop and will not get any files when it's getting to the directory in windows 10: Documents and Settings

Directory.GetFiles(textBox3.Text, "*.*", SearchOption.AllDirectories).ToList();

Since I didn't find a way to work around with Directory.GetFiles on pass over this directory I'm trying to use the recursive method.

2

2 Answers

1
votes

You can get file extension using extension = Path.GetExtension(fileName); and remove duplicates by using .Distinct() on files list like this:

IEnumerable<string> SearchAccessibleFiles(string root, string searchTerm)
    {
        var files = new List<string>();

        foreach (var file in Directory.EnumerateFiles(root).Where(m => m.Contains(searchTerm)))
        {
            string extension = Path.GetExtension(file);
            files.Add(extension);
        }
        foreach (var subDir in Directory.EnumerateDirectories(root))
        {
            try
            {
                files.AddRange(SearchAccessibleFiles(subDir, searchTerm));
            }
            catch (UnauthorizedAccessException ex)
            {
                // ...
            }
        }
        return files.Distinct().ToList();
    }

you can simply remove .Where(m => m.Contains(searchTerm)) part for searching without a search term.

Edit If you don't want to use .Distict() and want to check duplicates on the go you can try this method:

IEnumerable<string> SearchAccessibleFilesNoDistinct(string root, List<string> files)
{
    if(files == null)
       files = new List<string>();

    foreach (var file in Directory.EnumerateFiles(root))
    {
        string extension = Path.GetExtension(file);
        if(!files.Containes(extension))
           files.Add(extension);
    }
    foreach (var subDir in Directory.EnumerateDirectories(root))
    {
        try
        {
            SearchAccessibleFilesNoDistinct(subDir, files);
        }
        catch (UnauthorizedAccessException ex)
        {
            // ...
        }
    }
    return files;
}

and first time call looks like this:

var extensionsList = SearchAccessibleFilesNoDistinct("rootAddress",null);

you can see that I passed files list through the recursive method, by this approach we have the same files list, in all recursive calls so that should do the trick, keep in mind that in recursive calls there is no need to get the returned list as we have the same list already,but in the end we can use the returned list for further use. hope that helps

1
votes

The problem can be break down to a several parts:

(1) Enumerate recursively all the accessible directories
(2) Enumerate multiple directory files
(3) Get a distinct file extensions

Note that only the (3) is specific, (1) and (2) are general and can be used for other processing (like your SearchAccessibleFiles etc.). So let solve them separately:

(1) Enumerate recursively all the accessible directories:

This in turn can be split in two parts:

(A) Enumerate recursively a generic tree structure
(B) Specialization of the above for accessible directory tree

For (A) I personally use the helper method from my answer to How to flatten tree via LINQ? and similar:

public static class TreeUtils
{
    public static IEnumerable<T> Expand<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
    {
        var stack = new Stack<IEnumerator<T>>();
        var e = source.GetEnumerator();
        try
        {
            while (true)
            {
                while (e.MoveNext())
                {
                    var item = e.Current;
                    yield return item;
                    var elements = elementSelector(item);
                    if (elements == null) continue;
                    stack.Push(e);
                    e = elements.GetEnumerator();
                }
                if (stack.Count == 0) break;
                e.Dispose();
                e = stack.Pop();
            }
        }
        finally
        {
            e.Dispose();
            while (stack.Count != 0) stack.Pop().Dispose();
        }
    }
}

and here is the specialization for our case:

public static partial class DirectoryUtils
{
    public static IEnumerable<DirectoryInfo> EnumerateAccessibleDirectories(string path, bool all = false)
    {
        var filter = Func((IEnumerable<DirectoryInfo> source) => 
            source.Select(di =>
            {
                try { return new { Info = di, Children = di.EnumerateDirectories() }; }
                catch (UnauthorizedAccessException) { return null; }
            })
            .Where(e => e != null));

        var items = filter(Enumerable.Repeat(new DirectoryInfo(path), 1));
        if (all)
            items = items.Expand(e => filter(e.Children));
        else
            items = items.Concat(items.SelectMany(e => filter(e.Children)));
        return items.Select(e => e.Info);
    }

    static Func<T, TResult> Func<T, TResult>(Func<T, TResult> func) { return func; }
}

(2) Enumerate multiple directory files:

A simple extension methods to eliminate the repetitive code:

partial class DirectoryUtils
{
    public static IEnumerable<FileInfo> EnumerateAccessibleFiles(string path, bool allDirectories = false)
    {
        return EnumerateAccessibleDirectories(path, allDirectories).EnumerateFiles();
    }

    public static IEnumerable<FileInfo> EnumerateFiles(this IEnumerable<DirectoryInfo> source)
    {
        return source.SelectMany(di => di.EnumerateFiles());
    }
}

(3) Get a distinct file extensions:

With the above helpers, this is a matter of a simple LINQ query:

var result = DirectoryUtils.EnumerateAccessibleFiles(rootPath, true)
    .Select(file => file.Extension).Distinct()
    .ToList();

Finally, just for comparison, here is how your original method will look when using the same helpers:

IEnumerable<string> SearchAccessibleFiles(string root, string searchTerm)
{
    return DirectoryUtils.EnumerateAccessibleFiles(rootPath, true)
        .Where(file => file.FullName.Contains(searchTerm))
        .Select(file => file.FullName);
}

If the search term is not supposed to include directory info, you can change the filter condition to file.Name.Contains(searchTerm).