tags:

views:

302

answers:

6

Hello,

Using .NET 3.0, I've got the method below which correctly returns a collection of all of the files and directories (and sub-directories) of a specified directory. I'd like to, if possible, dumb this down to only use constructs that I'm pretty comfortable with. Specifically, here are the things I'm not clear on:

1. IEnumerable<FileSystemInfo>: I'd like to return List<FileSystemInfo> instead
2. Stack<FileSystemInfo>: I'd list to use List<FileSystemInfo> instead.
3. yield return: I've never used this before

.

public static IEnumerable<FileSystemInfo> GetAllFilesAndDirectories ( string dir ) {

    DirectoryInfo dirInfo = new DirectoryInfo( dir );
    Stack<FileSystemInfo> stack = new Stack<FileSystemInfo>();

    stack.Push( dirInfo );
    while ( dirInfo != null || stack.Count > 0 ) {
        FileSystemInfo fileSystemInfo = stack.Pop();
        DirectoryInfo subDirectoryInfo = fileSystemInfo as DirectoryInfo;
        if ( subDirectoryInfo != null ) {
            yield return subDirectoryInfo;
            foreach ( FileSystemInfo fsi in subDirectoryInfo.GetFileSystemInfos() ) {
                stack.Push( fsi );
            }
            dirInfo = subDirectoryInfo;
        } else {
            yield return fileSystemInfo;
            dirInfo = null;
        }
    }

}

.

The argument could be made that I should just get comfortable with the code above, but that's not what I'm shooting for today.

Thanks in advance

A: 

I believe you're looking for the existing method GetFileSystemInfos(string, SearchOptions). If you specify AllDirectories as the SearchOptions value it will recursively search the passed in folder.

For Example:

public static List<FileSystemInfo> GetAllFilesAndDirectories ( string dir ) {
  DirectoryInfo info = new DirectoryInfo(dir);
  FileSystemInfo[] all = info.GetFileSystemInfos("*", SearchOptions.AllDirectories);
  return new List<FileSystemInfo>(all);
}

If you want to write it out the long way though you can do the following

public static List<FileSystemInfo> GetAllFilesAndDirectories ( string dir ) {
  int i = 0; 
  List<DirectoryInfo> toProcess = new List<DirectoryInfo>();
  List<FileSystemInfo> list = new List<FileSystemInfo>();
  toProcess.Add(new DirectoryInfo(dir));
  while ( i < toProcess.Count ) { 
    DirectoryInfo curDir = toProcess[i];
    foreach ( FileSystemInfo curFile in curDir.GetFileSystemInfos() ) {
      list.Add(curFile);
      DirectoryInfo maybe = curFile as DirectoryInfo;
      if ( maybe != null ) {
        toProcess.Add(maybe);
      }
    i++;
  }
  return list;
}

FileSystemInfo[] all = info.GetFileSystemInfos("*", SearchOptions.AllDirectories); return new List(all); }

JaredPar
@bobbymcr, AllDirectories is a recursive search function according to the documentation http://msdn.microsoft.com/en-us/library/ms143448(VS.100).aspx
JaredPar
That doesn't work. The OP apparently wants to get all files and directories in all subfolders, starting at the given path. This will return the first level only. Note that there is no overload of GetFileSystemInfos which can accept a SearchOptions enum.
bobbymcr
Oops, correction, there is no way to do this until .NET 4.0. So it will eventually be possible...
bobbymcr
Thanks for your answer. Trying the "if you want to write it out the long way" code, it does not seem to include sub-directories and their contents.
Adam Kane
+1  A: 

Here is the shortest way I can think of:

static List<FileSystemInfo> GetAllFilesAndDirectories(string dir)
{
    DirectoryInfo dirInfo = new DirectoryInfo(dir);            
    List<FileSystemInfo> allFilesAndDirectories = new List<FileSystemInfo>();

    allFilesAndDirectories.AddRange(dirInfo.GetFiles("*", SearchOption.AllDirectories));
    allFilesAndDirectories.AddRange(dirInfo.GetDirectories("*", SearchOption.AllDirectories));

    return allFilesAndDirectories;
}

It will return a list of all files and directories at all levels, starting from the given path. The order it returns them in would be all files, then all directories.

bobbymcr
+1: right to the point
Rubens Farias
A: 

You had asked specifically to "dumb this down". I think the other two answers are very good, but here's another way to do it with even more basic, easily understandable, at the beginning level code.

http://support.microsoft.com/kb/303974

Edit

I know this is not 3.0, but it's still the tried, tested, and easy to understand way of doing this.

David Stratton
+1  A: 
    public List<Object> GetFilesAndDirectories(string path)
    {
        List<Object> lst = new List<Object>();
        string[] dirs = null;

        try
        {
            dirs = Directory.GetDirectories(path);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }

        foreach (string d in dirs)
        {
            string[] files = null;

            try
            {
                files = Directory.GetFiles(d);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            foreach (string f in files)
            {
                lst.Add(f);
            }

            lst.Add(d);

            lst.AddRange(GetFilesAndDirectories(d));
        }

        return lst;
    }


List<Object> stuff = GetFilesAndDirectories(someRoot);
byte
I would point out that it's not best to just `catch { }` exceptions though, best to at least log them.
RCIX
Sure. Also, this was written with a generic object type for the list in case you wanted to store actual file/directory objects instead of string representations. Customize to suit your needs.
byte
Kirk Broadhurst
"Empty catch" is generally the wrong thing to do. This will catch anything. Just catch the exceptions that the method can actually throw. In this case, it would probably be IOException (error reading the disk, or directory was removed before it could be listed), UnauthorizedAccessException (for directories that the user cannot list).
bobbymcr
@Kirk Broadhurst - Indeed. Altered post to reflect.
byte
A: 

If you wanna a "beginner-friendly method", I can suggest bobbymcr method.

But, if you want to keep your code structure, I reproduce below code I posted in your following question:

static IEnumerable<FileSystemInfo> GetAllFilesAndDirectories(string path)
{
    string currentDirectory = "";
    string[] files = Directory.GetFiles( // skip empty subfolders
        path, "*.*", SearchOption.AllDirectories);
    foreach (string file in files)
    {
        if(currentDirectory != Path.GetDirectoryName(file))
        {
            // First time in this directory: return it
            currentDirectory = Path.GetDirectoryName(file);
            yield return new DirectoryInfo(currentDirectory);
        }

        yield return new FileInfo(file);
    }
}
Rubens Farias
A: 

I think from a code readability point of view, your best bet is to write a recursive function. A recursive function is one that calls itself, till it reaches a point where it does not need to call any other function.

To illustrate, the factorial of n, written as n! and defined to be the quantity 1 x 2 x 3 x ... x n (where n is a positive integer) can be quite easily defined in a recursive manner, as follows.

public int factorial(int n)
{
    if (n < 0)
    {
        throw new Exception("A factorial cannot be calculated for negative integers.");
    }

    if (n == 0 || n == 1)
    {
        // end condition, where we do not need to make a recursive call anymore
        return 1;
    }
    else
    {
        // recursive call
        return n * factorial(n - 1);
    }
}

NB: 0! and 1! are defined to be 1.

Likewise, a method to enumerate all files and folder under a given path can also be defined recursively. This is because files and folders have a recursive structure.

Therefore a method such as follows, would work:

public static List<FileSystemInfo> GetAllFilesAndFolders(string folder)
{
    // NOTE : We are performing some basic sanity checking
    // on the method's formal parameters here
    if (string.IsNullOrEmpty(folder))
    {
        throw new ArgumentException("An empty string is not a valid path.", "folder");
    }
    if (!Directory.Exists(folder))
    {
        throw new ArgumentException("The string must be an existing path.", "folder");
    }

    List<FileSystemInfo> fileSystemInfos = new List<FileSystemInfo>();

    try
    {
        foreach (string filePath in Directory.GetFiles(folder, "*.*"))
        {
            // NOTE : We will add a FileSystemInfo object for each file found
            fileSystemInfos.Add(new FileInfo(filePath));
        }
    }
    catch
    {
        // NOTE : We are swallowing all exceptions here
        // Ideally they should be surfaced, and at least logged somewhere
        // Most of these will be security/permissions related, i.e.,
        // the Directory.GetFiles method will throw an exception if it
        // does not have security privileges to enumerate files in a folder.
    }
    try
    {
        foreach (string folderPath in Directory.GetDirectories(folder, "*"))
        {
            // NOTE : We will add a FileSystemInfo object for each directory found
            fileSystemInfos.Add(new DirectoryInfo(folderPath));

            // NOTE : We will also add all FileSystemInfo objects found under
            // each directory we find
            fileSystemInfos.AddRange(GetAllFilesAndFolders(folderPath));
        }
    }
    catch
    {
        // NOTE : We are swallowing all exceptions here
        // Ideally they should be surfaced, and at least logged somewhere
        // Most of these will be security/permissions related, i.e.,
        // the Directory.GetDirectories method will throw an exception if it
        // does not have security privileges to enumerate files in a folder.
    }

    return fileSystemInfos;
}

One thing to note is that this method will "walk" the entire directory structure underneath the folder and will NOT return until it has "walked" the entire heirarchy. Therefore, it might take a long time to return, if there are many objects to be found.

Another thing to note is that the readability of this method can be further improved by using Lambda expressions and extention methods.

NB: The trouble with using Directory.GetFiles and Directory.GetDirectories to recurse sub-folders is that if there are any exceptions thrown (e.g., related to security permissions) the method will return nothing, whereas recursing manually allows one to handle those exceptions and still get a set of files back.

Umar Farooq Khawaja