您确实应该使用新的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 秒内完成。
希望对你有所帮助!