views:

195

answers:

4

The end goal is to have some form of a data structure that stores a hierarchal structure of a directory to be stored in a txt file.

I'm using the following code and so far, and I'm struggling with combining dirs, subdirs, and files.

/// <summary>
/// code based on http://msdn.microsoft.com/en-us/library/bb513869.aspx
/// </summary>
/// <param name="strFolder"></param>
public static void TraverseTree ( string strFolder )
{
  // Data structure to hold names of subfolders to be
  // examined for files.
  Stack<string> dirs = new Stack<string>( 20 );

  if ( !System.IO.Directory.Exists( strFolder ) )
  {
    throw new ArgumentException();
  }
  dirs.Push( strFolder );

  while ( dirs.Count > 0 )
  {
    string currentDir = dirs.Pop();
    string[] subDirs;
    try
    {
      subDirs = System.IO.Directory.GetDirectories( currentDir );
    }

    catch ( UnauthorizedAccessException e )
    {
      MessageBox.Show( "Error: " + e.Message );
      continue;
    }
    catch ( System.IO.DirectoryNotFoundException e )
    {
      MessageBox.Show( "Error: " +  e.Message );
      continue;
    }

    string[] files = null;
    try
    {
      files = System.IO.Directory.GetFiles( currentDir );
    }

    catch ( UnauthorizedAccessException e )
    {
      MessageBox.Show( "Error: " +  e.Message );
      continue;
    }

    catch ( System.IO.DirectoryNotFoundException e )
    {
      MessageBox.Show( "Error: " + e.Message );
      continue;
    }
    // Perform the required action on each file here.
    // Modify this block to perform your required task.
    /*
    foreach ( string file in files )
    {
      try
      {
        // Perform whatever action is required in your scenario.
        System.IO.FileInfo fi = new System.IO.FileInfo( file );
        Console.WriteLine( "{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime );
      }
      catch ( System.IO.FileNotFoundException e )
      {
        // If file was deleted by a separate application
        //  or thread since the call to TraverseTree()
        // then just continue.
        MessageBox.Show( "Error: " +  e.Message );
        continue;
      }
    } 
    */

    // Push the subdirectories onto the stack for traversal.
    // This could also be done before handing the files.
    foreach ( string str in subDirs )
      dirs.Push( str );

    foreach ( string str in files )
      MessageBox.Show( str );
  }
+5  A: 

You can use a sort of Composite pattern where a Composite item - is a folder.

Here is a sample code, that builds Tree structure of target folder. It works recursively, and consumes a bit more memory, but simplicity worth it.

class TreeItem
{
    public string FolderName;
    public List<TreeItem> SubFolders = new List<TreeItem>();
    public string[] Files;
}

class Program
{

    private static TreeItem FileTree(string rootFolder){
        var item = new TreeItem();
        item.FolderName = rootFolder;
        item.Files = System.IO.Directory.GetFiles(rootFolder);

        foreach(var folder in System.IO.Directory.GetDirectories(rootFolder))
        {
            item.SubFolders.Add(FileTree(folder));
        }
        return item;
    }

    //Traversal algorithm
    private static void PrintComposite(TreeItem node, int ident)
    {
        var dirName = System.IO.Path.GetFileName(node.FolderName);
        Console.WriteLine(@"{0}{1}", new string('-', ident), dirName);
        foreach(var subNode in node.SubFolders)
        {
            PrintComposite(subNode, ident + 1);
        }
    }

    public static void Main(string[] args)
    {
        var tree = FileTree(@"D:\Games");
        PrintComposite(tree,0);
    }   
}
Valera Kolupaev
This works excellent; however, I also want to add files to the listing, I've been playing around with printnode and adding another foreach, i'll see if i can get it work .. so far this is great
dassouki
+1  A: 

For one thing, I think you need to make more objects. A DirectoryElementInterface interface or abstract class and a DirectoryElement object, and a FileElement object that implement DirectoryElementInterface. Now, rather than using a stack to iterate through the heirarchy, create DirectoryElementInterface root = new DirectoryElement(nameOfNode). Then for every file in getFiles do something like root.addElement(new FileElement(filename));. addElement should add to a List within the DirectoryElement. Do similarly for the directories. OK, now you can create one level.

Now for the iteration step. Take the routine you just wrote and make root a parameter. You can call it anything but for this discussion I will be calling this new routine addDirectoryInformation. Your main will now be the creation of the root and calling addDirectoryInformation passing in the root. To iterate we need to ask the now filled in root for its list of elements, do a foreach over the list and call addDirectoryInformation for each of the elements that is a directory. Once you have that working, move the loop into the end of addDirectoryInformation. Now every directory you add adds all its children recursively.

One more thing for a proper recursive program. You have to know when to stop recursing. In this case it's easy. If there are no directories in the list addDirectoryInformation never gets called. So you are done.

verisimilidude
Initial task is a bit unclear, wouldn't such complexity be and overkill?Composite pattern is designed to help unify treating each node and leaf consistently, but now, it is not required.
Valera Kolupaev
A: 

I got it working using code based on http://weblogs.asp.net/israelio/archive/2004/06/23/162913.aspx

// How much deep to scan. (of course you can also pass it to the method)
const int HowDeepToScan=20;

public static void ProcessDir ( string dirName, int recursionLvl, string strFileName)
{

  string tabs = new String( '-', recursionLvl );

  if ( recursionLvl<=HowDeepToScan )
  {
    // Process the list of files found in the directory. 
    string [] fileEntries = Directory.GetFiles( dirName );
    TextWriter tw = new StreamWriter( strFileName, true );
    tw.WriteLine( tabs + "<a href=\" " +  System.IO.Path.GetFullPath( dirName ) + "\">" + System.IO.Path.GetFileName( dirName ) + "</a><br />" );
    foreach ( string fileName in fileEntries )
    {
      // do something with fileName

      tw.WriteLine( tabs + "<a href=\" " +  System.IO.Path.GetFullPath( fileName ) + "\">" + System.IO.Path.GetFileName( fileName ) + "</a><br />" );

    }
    tw.Close();

    // Recurse into subdirectories of this directory.
    string [] subdirEntries = Directory.GetDirectories( dirName );
    foreach ( string subdir in subdirEntries )
      // Do not iterate through reparse points
      if ( ( File.GetAttributes( subdir ) &
        FileAttributes.ReparsePoint ) !=
            FileAttributes.ReparsePoint )

        ProcessDir( subdir, recursionLvl+1, strFileName );

  }
}

output

<a href=" C:\code">code</a><br />
<a href=" C:\code\group.zip">FluentPath (1).zip</a><br />
<a href=" C:\code\index.html">index.html</a><br />
dassouki
A: 

I did a course last week where we did something similar, the output was to console but no reason you can't streamwrite it to a .txt file.

using System; using System.Collections.Generic; using System.Linq; using System.Text;

namespace ShowDirectory { class Program { static void Main(string[] args) { Console.WriteLine("This program lists all the files in the directory."); System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(@"C:\"); foreach (System.IO.FileInfo file in dir.GetFiles(".")) { Console.WriteLine("{0}, {1}", file.Name, file.Length); } Console.ReadLine(); } } }

Stephen Murby