Reputation: 43
I am trying to generate a list of all the files present in a directory and its sub-directories along with their creation date. The date and file name to be separated by "::"
I tried the below code but its taking time when the number of files are huge. Can anyone suggest me a better approach/ optimise the code??
I am even getting the "Access to the file denied" exception with the below approach, for some of the files in 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();
Upvotes: 2
Views: 4060
Reputation: 44906
You really should use the new EnumerateFiles overloads in order to avoid getting the entire list in memory.
foreach (var f in di.EnumerateFiles("*", SearchOption.AllDirectories)) {
write.WriteLine(f.FullName + "::" + f.CreationTime.ToShortDateString());
}
You can further optimize your code by writing each line as it is enumerated over. You can avoid the stream manipulation all together. Your entire routine becomes just a few lines:
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);
}
}
Ok, so not having .Net 4.0 is a bummer. However, you could write your own class to enumerate a file system without too much trouble. Here is one I just wrote that you can play with, and it only uses API calls that are already available to .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;
}
}
Using it is pretty easy, just instantiate it with the options you want, and then use it like any 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);
Now obviously this isn't as efficient as the .Net 4.0 one, but the memory footprint should be acceptable. I tested this on a directory with 50K+ files and it finished in about 5 seconds.
Hope this helps you out!
Upvotes: 1
Reputation: 2683
Or when you're just lookin for the path you can do
Directory.GetFiles("C:\\", "*", SearchOption.AllDirectories)
.ToList()
.ForEach(Console.WriteLine);
Upvotes: 1
Reputation: 8706
var dirinfo = new DirectoryInfo( "c:\path" );
var entries = dirinfo.GetFileSystemInfos( "*.*", SearchOption.AllDirectories )
.Select( t => string.Format( "{0}::{1}", t.FullName, t.CreationTime );
Upvotes: 2