Question

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 );
  }
Was it helpful?

Solution 3

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 />

OTHER TIPS

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);
    }   
}

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.

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(); } } }

One of the approaches is to use iterator over files tree like this:

// IncludeExcludeFileEnumerator(string baseDir, string includePattern, string excludePattern)
// Include pattern can include ** that means tree hierarchy
var myFiles = new IncludeExcludeFileEnumerable(@"C:\test\aaa", @"**.bmp,*.jpg", "*excl_bad*.*,*fu*");
foreach (var s in myFiles)
{
    Console.Out.WriteLine(s);
}

Code for file iterator ( IEnumerator, IEnumerable ):

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

namespace IncludeExcludeFileEnumerator
{
    public class IncludeExcludeFileEnumerator : IEnumerator<String>
    {
        private string excludeRegExPattern;
        private readonly Regex regexSeparateFilePath;
        private readonly Regex excludeRegex = null;
        private int currentPatternIndex;
        private IEnumerator<string> filesEnum;
        private IEnumerable<string> files;
        bool isNext = true;
        private readonly List<Tuple<string, string, SearchOption>> incPatternsList;


        public IncludeExcludeFileEnumerator(string baseDirectory, string includePattern, string excludePattern)
        {
            // Split comma separated string to array of include patterns
            var initIncludePatterns = includePattern.Split(',');
            regexSeparateFilePath = new Regex(@"(.*)[\\/]([^\\/]*$)", RegexOptions.Compiled);

            // Prepare include patterns
            incPatternsList = initIncludePatterns.ToList().ConvertAll(
                (incPattern) =>
                {
                    incPattern = incPattern.Trim();
                    var matches = regexSeparateFilePath.Matches(incPattern);
                    string pathPattern;
                    string filePattern;
                    if (matches.Count == 0)
                    {
                        pathPattern = "";
                        filePattern = incPattern;
                    }
                    else
                    {
                        pathPattern = matches[0].Groups[1].Value;
                        filePattern = matches[0].Groups[2].Value;
                    }
                    SearchOption searchOption = SearchOption.TopDirectoryOnly;
                    if (filePattern.Contains("**"))
                    {
                        filePattern = filePattern.Replace("**", "*");
                        searchOption = SearchOption.AllDirectories;
                    }
                    var fullPathPattern = Path.Combine(baseDirectory, pathPattern);
                    // Returns tuple {PathPattern, FilePattern, SearchOption}
                    return new Tuple<string, string, SearchOption>(fullPathPattern, filePattern, searchOption);
                });

            // Prepare regular expression for exclude case (all in one, concatinated by (| - or) separator)
            if (!String.IsNullOrWhiteSpace(excludePattern))
            {
                var excPatterns = excludePattern.Replace(".", @"\.");
                excPatterns = excPatterns.Replace("*", ".*");
                excludeRegExPattern = excPatterns.Replace(",", "|");
                excludeRegex = new Regex(excludeRegExPattern, RegexOptions.Compiled);
            }
            Reset();
        }

        public string Current
        {
            get { return filesEnum.Current; }
        }

        public void Dispose()
        {

        }

        object System.Collections.IEnumerator.Current
        {
            get { return (Object)this.Current; }
        }

        public bool MoveNext()
        {
            do
            {
                if (( filesEnum == null ) && (incPatternsList.Count < currentPatternIndex + 2))
                {
                    return false;
                }
                if ((filesEnum == null) || (isNext == false))
                {
                    var tuple = incPatternsList[++currentPatternIndex];
                    files = Directory.EnumerateFiles(tuple.Item1, tuple.Item2, tuple.Item3);
                    filesEnum = files.GetEnumerator();
                    isNext = true;
                }
                while (isNext)
                {
                    isNext = filesEnum.MoveNext();
                    if (isNext) 
                    {
                        if (excludeRegex==null) return true;
                        if (!excludeRegex.Match(filesEnum.Current).Success) return true;
                        // else continue;
                    }
                    else
                    {
                        filesEnum = null;
                    }
                }
            } while (true);
        }

        public void Reset()
        {
            currentPatternIndex = -1;
            filesEnum = null;
        }
    }

    public class IncludeExcludeFileEnumerable : IEnumerable<string>
    {
        private string baseDirectory;
        private string includePattern;
        private string excludePattern;

        public IncludeExcludeFileEnumerable(string baseDirectory, string includePattern, string excludePattern)
        {
            this.baseDirectory = baseDirectory;
            this.includePattern = includePattern;
            this.excludePattern = excludePattern;
        }

        public IEnumerator<string> GetEnumerator()
        {
            return new IncludeExcludeFileEnumerator(baseDirectory, includePattern, excludePattern);
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (IEnumerator)this.GetEnumerator();
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top