C # - Как создать список имен файлов и подкаталогов в каталоге с датой создания - PullRequest
2 голосов
/ 22 сентября 2011

Я пытаюсь создать список всех файлов, представленных в каталоге и его подкаталогах, а также дату их создания.Дата и имя файла должны быть разделены "::"

. Я попробовал приведенный ниже код, но это занимает много времени, когда количество файлов огромно.Может кто-нибудь предложить мне лучший подход / оптимизировать код ??

Я даже получаю исключение "Доступ к файлу запрещен" с приведенным ниже подходом, для некоторых файлов на диске C.

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 ]

2 голосов
/ 22 сентября 2011
var dirinfo = new DirectoryInfo( "c:\path" );
var entries = dirinfo.GetFileSystemInfos( "*.*", SearchOption.AllDirectories )
    .Select( t => string.Format( "{0}::{1}", t.FullName, t.CreationTime );
1 голос
/ 22 сентября 2011

Вы действительно должны использовать новые 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);
    }
}

UPDATE

Хорошо, так что отсутствие .Net 4.0 - облом. Однако, вы можете написать свой собственный класс для перечисления файловой системы без особых проблем . Вот тот, который я только что написал, с которым можно играть, и он использует только вызовы API, которые уже доступны для .Net 3.5

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 секунд.

Надеюсь, это поможет вам!

1 голос
/ 22 сентября 2011

Или когда вы просто ищете путь, который вы можете сделать

Directory.GetFiles("C:\\", "*", SearchOption.AllDirectories)
                .ToList()
                .ForEach(Console.WriteLine);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...