【问题标题】:Beginner - C# iteration through directory to produce a file list初学者 - C# 遍历目录以生成文件列表
【发布时间】:2010-04-28 19:05:35
【问题描述】:

最终目标是拥有某种形式的数据结构,用于存储要存储在 txt 文件中的目录的层次结构。

到目前为止,我一直在使用以下代码,但我正在努力组合目录、子目录和文件。

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

【问题讨论】:

  • 我认为您必须更清楚地说明您的问题。代码乍一看还不错。
  • 顺便说一句,该算法使用显式堆栈迭代地工作,而不是递归地工作。
  • 不要害怕递归!
  • 我不太明白你想做什么。然而,你的问题让我想起了我最近看到的这个很酷的项目,叫做Fluent Pathweblogs.asp.net/bleroy/archive/2010/03/10/…

标签: c# file-structure


【解决方案1】:

您可以使用一种Composite pattern,其中复合项目 - 是一个文件夹。

这是一个示例代码,用于构建目标文件夹的树结构。它以递归方式工作,并消耗更多内存,但值得简单。

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

【讨论】:

  • 这很好用;但是,我还想将文件添加到列表中,我一直在玩 printnode 并添加另一个 foreach,我会看看我是否可以让它工作.. 到目前为止这很棒
【解决方案2】:

一方面,我认为您需要制作更多对象。一个 DirectoryElementInterface 接口或抽象类和一个 DirectoryElement 对象,以及一个实现 DirectoryElementInterface 的 FileElement 对象。现在,与其使用堆栈来遍历层次结构,不如创建DirectoryElementInterface root = new DirectoryElement(nameOfNode)。然后对 getFiles 中的每个文件执行root.addElement(new FileElement(filename)); 之类的操作。 addElement 应添加到 DirectoryElement 中的列表。对目录执行类似操作。好的,现在您可以创建一个级别。

现在进行迭代步骤。拿你刚才写的例程,把root作为参数。您可以将其称为任何名称,但对于本次讨论,我将调用此新例程 addDirectoryInformation。您现在的主要任务是创建根并调用传递根的 addDirectoryInformation。要进行迭代,我们需要向现在填充的根请求其元素列表,对列表执行 foreach 并为每个作为目录的元素调用 addDirectoryInformation。完成该工作后,将循环移动到 addDirectoryInformation 的末尾。现在,您添加的每个目录都会递归地添加其所有子目录。

对于正确的递归程序还有一件事。你必须知道什么时候停止递归。在这种情况下,这很容易。如果列表中没有目录,则永远不会调用 addDirectoryInformation。这样你就完成了。

【讨论】:

  • 初始任务有点不清楚,这么复杂会不会有点矫枉过正?复合模式旨在帮助统一处理每个节点和叶,但现在不是必需的。
【解决方案3】:

我使用基于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 );

  }
}

输出

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

【讨论】:

    【解决方案4】:

    上周我做了一个课程,我们做了类似的事情,输出是控制台,但没有理由不能将它流式写入 .txt 文件。

    使用系统; 使用 System.Collections.Generic; 使用 System.Linq; 使用 System.Text;

    命名空间 ShowDirectory { 课堂节目 { 静态无效主要(字符串 [] 参数) { Console.WriteLine("这个程序列出了目录下的所有文件。"); System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(@"C:\"); foreach (System.IO.FileInfo 文件在 dir.GetFiles(".")) { Console.WriteLine("{0}, {1}", file.Name, file.Length); } Console.ReadLine(); } } }

    【讨论】:

      【解决方案5】:

      其中一种方法是在文件树上使用迭代器,如下所示:

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

      文件迭代器代码(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();
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多