Processing Folders Iteratively


Click here to change the theme.

DirectoryIterator.zip

This article shows how to retrieve subdirectories of a Windows directory iteratively instead of recursively. Typically, recursion is used to retrieve hierarchically recursive data such as directories. Please see my article Retrieving Hierarchically Recursive Data Iteratively for a description of retrieving hierarchically recursive data iteratively.

The example for this article retrieves the directories of the user's Internet Explorer Favorites but could easily be adapted for other directories. If you are like me, your favorites are organized into many folders. This example does not retrieve the actual favorites; they are individual files within the directories. Reading the files (favorites) would be an easy addition.

To retrieve the directories, a class called "Folder" is used with the following members:

Name Type Description
Name string Unqualified folder name
Parent Folder Recursive reference to a parent folder
Attributes FileAttributes  
CreationTime DateTime  
LastAccessTime DateTime  
LastWriteTime DateTime  
Children List<Folder> Subdirectories
Level int Hierarchical depth, used only for formatting output
GetFolder Stack<Folder> ToDo, string root Gets the subdirectories of the directory
Qualify string Creates an absolute path
Unqualify string Gets just the unqualified name of a directory
PutFolder(Stack<Folder> ToDo, StreamWriter sw) void Writes the folder to the text file and queues the children

Note that the Name is just the unqualified name and does not include the qualification.

The Internet Explorer Favorites are usually stored in the "C:\Users\{User}\Favorites" directory, so Name in the Folder object for the top folder to be processed is just "Favorites". The Internet Explorer Favorites are usually stored in the "C:\Users\{User}\Favorites" directory. We use the following to get the "C:\Users\{User}" portion:

Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

To begin the iteration, we first create a "Folder" object for the top folder, "Favorites ", and push it onto the stack. After pushing "Favorites" onto the stack, we begin the iteration that pops it off the stack and processes it. We process each object (folder) by retrieving its children and pushing each of them onto the stack of folders to do.

The sample program retrieves the folders into "Folder" objects then uses (shows) a separate loop to write them to a text file. Note that you will need to change the name of the file in the sample program.

The following is the Main method of the console program:

string RootFolderName = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

Folder TopFolder = new Folder();
TopFolder.Name = "Favorites";
ToDo.Push(TopFolder);
try {sw = new StreamWriter(OutFilename);}
catch (Exception ex)
{
    Console.WriteLine("Output file error: {0}", ex.Message);
    return;
}
// Get the folders
Console.WriteLine("\tGetting");
while (ToDo.Count > 0)
{
    (ToDo.Pop()).GetFolder(ToDo, RootFolderName);
    // The following will write something to the console
    // just so we know the program is working.
    Console.WriteLine("\tToDo.Count={0}", ToDo.Count);
}
// Put the folders. Start by creating the ToDo stack again.
Console.WriteLine("\tPutting");
ToDo.Push(TopFolder);
while (ToDo.Count > 0)
{
    (ToDo.Pop()).PutFolder(ToDo, sw);
    // The following will write something to the console
    // just so we know the program is working.
    Console.WriteLine("ToDo.Count={0}", ToDo.Count);
}
sw.Flush();

Where the "ToDo" stack is:

static Stack<Folder> ToDo = new Stack<Folder>();

And "sw" is a StreamWriter as in:

static StreamWriter sw = null;

The following is the "Folder" class:

class Folder
{
    public string Name;
    public Folder Parent = null;
    public FileAttributes Attributes { get; set; }
    public DateTime CreationTime { get; set; }
    public DateTime LastAccessTime { get; set; }
    public DateTime LastWriteTime { get; set; }
    public List<Folder> Children = new List<Folder>();
    int Level = 0;        // only for formatting output

    /// <summary>
    /// Gets the information for a folder including the subdirectories.
    /// A new object is created for each subdirectory and each of them
    /// is added to the Children for this directory and to the ToDo stack.
    /// </summary>
    /// <param name="ToDo"></param>
    internal void GetFolder(Stack<Folder> ToDo, string root)
    {
        string Path = root + '\\' + Qualify();
        DirectoryInfo di = new DirectoryInfo(Path);
        Attributes = di.Attributes;
        CreationTime = di.CreationTime;
        LastAccessTime = di.LastAccessTime;
        LastWriteTime = di.LastWriteTime;
        List<string> dirs = new List<string>(Directory.EnumerateDirectories(Path));
        foreach (string fn in dirs)
        {
            Folder sf = new Folder();
            sf.Name = Folder.Unqualify(fn);
            sf.Parent = this;
            sf.Level = Level + 1;
            Children.Add(sf);
            ToDo.Push(sf);
        }
    }

    /// <summary>
    /// This will prefix the parent directory names separated
    /// by backslashes to create a fully qualified path. The
    /// result must be qualified by the root path.
    /// </summary>
    /// <param name="Folder"></param>
    /// <returns></returns>
    public string Qualify()
    {
        Folder TempFolder = this;
        Stack<string> names = new Stack<string>();
        names.Push(Name);
        while (TempFolder.Parent != null)
        {
            TempFolder = TempFolder.Parent;
            names.Push(TempFolder.Name);
        }
        return string.Join("\\", names.ToArray());
    }

    /// <summary>
    /// Since Directory.EnumerateDirectories returns the fully qualified path
    /// of each directory, we need to get just the unqualified name of a directory.
    /// </summary>
    /// <param name="dir">fully qualified path</param>
    /// <returns>unqualified path</returns>
    public static string Unqualify(string dir)
    {
        int i = dir.LastIndexOf('\\');
        return dir.Substring(i + 1);
    }

    internal void PutFolder(Stack<Folder> ToDo, StreamWriter sw)
    {
        string tabs = new string('\t', Level);
        sw.WriteLine("{0}{1} {2} {3}", tabs, Name, CreationTime, LastAccessTime);
        // since the stack is LIFO, the objects will be popped off in
        // reverse order from what they are pushed, so we will push
        // in reverse order 
        for (int i = Children.Count - 1; i >= 0; --i)
            ToDo.Push(Children[i]);
    }
}