C# - 如何使用创建日期生成目录中的文件名和子目录列表

时间:2011-09-22 13:16:28

标签: c# file directory

我正在尝试生成目录及其子目录中存在的所有文件及其创建日期的列表。要用“::”

分隔的日期和文件名

我尝试了以下代码,但是当文件数量很大时需要花时间。任何人都可以建议我更好的方法/优化代码??

对于C Drive中的某些文件,我甚至使用以下方法获取“访问文件被拒绝”例外。

DirectoryInfo dir1 = new DirectoryInfo(path);
DirectoryInfo[] dir = dir1.GetDirectories();
StreamWriter write = new StreamWriter("Test.lst");
foreach (DirectoryInfo di in dir)
{
    try
    {
        FileInfo[] file = di.GetFiles("*", SearchOption.AllDirectories);

        foreach (var f in file)
        {
            write.WriteLine(f.FullName + "::" + f.CreationTime.ToShortDateString());
        }
    }
    catch
    {
    }
}
write.Flush();
write.Close();

3 个答案:

答案 0 :(得分:2)

var dirinfo = new DirectoryInfo( "c:\path" );
var entries = dirinfo.GetFileSystemInfos( "*.*", SearchOption.AllDirectories )
    .Select( t => string.Format( "{0}::{1}", t.FullName, t.CreationTime );

答案 1 :(得分:1)

或者当你只是寻找可以做的路径时

Directory.GetFiles("C:\\", "*", SearchOption.AllDirectories)
                .ToList()
                .ForEach(Console.WriteLine);

答案 2 :(得分:1)

你真的应该使用新的EnumerateFiles重载,以避免将整个列表存入内存。

foreach (var f in di.EnumerateFiles("*", SearchOption.AllDirectories)) {
    write.WriteLine(f.FullName + "::" + f.CreationTime.ToShortDateString());
}

您可以通过编写枚举的每一行来进一步优化代码。您可以一起避免流操作。你的整个例程只有几行:

public static void GenerateList(String dirPath, String fileName) {
    var dir1 = new DirectoryInfo(dirPath);

    try {
        var lines = from f in dir1.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
                    select f.FullName + "::" + f.CreationTime.ToShortDateString();

        File.WriteAllLines(fileName, lines);
    }
    catch (Exception ex) {
        Console.WriteLine(ex);
    }
}

更新

好的,所以没有.Net 4.0是一个无赖。但是,您可以编写自己的类来枚举文件系统而不会有太多麻烦。这是我刚才写的一个你可以玩的,它只使用已经可用于 .Net 3.5的API调用

public class FileSystemInfoEnumerator: IEnumerator<FileSystemInfo>, IEnumerable<FileSystemInfo> {
  private const string DefaultSearchPattern = "*.*";

  private String InitialPath { get; set; }
  private String SearchPattern { get; set; }
  private SearchOption SearchOptions { get; set; }
  private Stack<IEnumerator<FileSystemInfo>> EnumeratorStack { get; set; }

  private Action<Exception> ErrorHandler { get; set; }

  public FileSystemInfoEnumerator(String path, String pattern = DefaultSearchPattern, SearchOption searchOption = SearchOption.TopDirectoryOnly, Action<Exception> errorHandler = null) {

     if (String.IsNullOrWhiteSpace(path))
        throw new ArgumentException("path cannot be null or empty");

     var dirInfo = new DirectoryInfo(path);

     if(!dirInfo.Exists)
        throw new InvalidOperationException(String.Format("File or Directory \"{0}\" does not exist", dirInfo.FullName));

     InitialPath = dirInfo.FullName;
     SearchOptions = searchOption;

     if(String.IsNullOrWhiteSpace(pattern)) {
        pattern = DefaultSearchPattern;
     }

     ErrorHandler = errorHandler ?? DefaultErrorHandler;
     EnumeratorStack = new Stack<IEnumerator<FileSystemInfo>>();
     SearchPattern = pattern;
     EnumeratorStack.Push(GetDirectoryEnumerator(new DirectoryInfo(InitialPath)));
  }

  private void DefaultErrorHandler(Exception ex) {
     throw ex;
  }

  private IEnumerator<FileSystemInfo> GetDirectoryEnumerator(DirectoryInfo directoryInfo) {
     var infos = new List<FileSystemInfo>();

     try {
        if (directoryInfo != null) {
           var info = directoryInfo.GetFileSystemInfos(SearchPattern);
           infos.AddRange(info);
        }
     } catch (Exception ex) {
        ErrorHandler(ex);
     }

     return infos.GetEnumerator();
  }

  public void Dispose() {
     foreach (var enumerator in EnumeratorStack) {
        enumerator.Reset();
        enumerator.Dispose();
     }
  }

  public bool MoveNext() {
     var current = Current;

     if (ShouldRecurse(current)) {
        EnumeratorStack.Push(GetDirectoryEnumerator(current as DirectoryInfo));
     }

     var moveNextSuccess = TopEnumerator.MoveNext();

     while(!moveNextSuccess && TopEnumerator != null) {
        EnumeratorStack.Pop();

        moveNextSuccess = TopEnumerator != null && TopEnumerator.MoveNext();
     }

     return moveNextSuccess;
  }

  public void Reset() {
     EnumeratorStack.Clear();
     EnumeratorStack.Push(GetDirectoryEnumerator(new DirectoryInfo(InitialPath)));
  }

  public FileSystemInfo Current {
     get {
        return TopEnumerator.Current;
     }
  }

  object IEnumerator.Current {
     get {
        return Current;
     }
  }

  public IEnumerator<FileSystemInfo> GetEnumerator() {
     return this;
  }

  IEnumerator IEnumerable.GetEnumerator() {
     return GetEnumerator();
  }

  IEnumerator<FileSystemInfo> TopEnumerator {
     get {
        if(EnumeratorStack.Count > 0)
           return EnumeratorStack.Peek();

        return null;
     }
  }

  private Boolean ShouldRecurse(FileSystemInfo current) {
     return current != null &&
            IsDirectory(current) &&
            SearchOptions == SearchOption.AllDirectories;
  }

  private Boolean IsDirectory(FileSystemInfo fileSystemInfo) {
     return fileSystemInfo != null &&
            (fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
  }
}

使用它非常简单,只需使用您想要的选项对其进行实例化,然后像任何IEnumerable一样使用它。

var fileSystemEnumerator = new FileSystemInfoEnumerator("C:\\Dir", 
    searchOption: SearchOption.AllDirectories,
    errorHandler: Console.WriteLine);

var lines = from f in fileSystemEnumerator
            select f.FullName + "::" + f.CreationTime.ToShortDateString();

File.WriteAllLines("FileNames.txt", lines);

现在显然这不如.Net 4.0那么有效,但内存占用应该是可以接受的。 我在一个包含50K +文件的目录上对此进行了测试,并在大约5秒内完成。

希望这会帮助你!