[英]C# GetFiles with Date Filter
有沒有更有效的方法來從帶有日期過濾器的目錄中填充文件名列表?
目前,我正在這樣做:
foreach (FileInfo flInfo in directory.GetFiles())
{
DateTime yesterday = DateTime.Today.AddDays(-1);
String name = flInfo.Name.Substring(3,4);
DateTime creationTime = flInfo.CreationTime;
if (creationTime.Date == yesterday.Date)
yesterdaysList.Add(name);
}
這會遍歷文件夾中的每個文件,我覺得應該有一種更有效的方法。
第一個解決方案:
您可以使用 LINQ:
List<string> yesterdaysList = directory.GetFiles().Where(x => x.CreationTime.Date == DateTime.Today.AddDays(-1))
.Select(x => x.Name)
.ToList();
然后你可以直接使用這個名字列表。
第二種解決方案:
另一個使其更快的解決方案可能是:
DateTime yesterday = DateTime.Today.AddDays(-1); //initialize this variable only one time
foreach (FileInfo flInfo in directory.GetFiles()){
if (flInfo.CreationTime.Date == yesterday.Date) //use directly flInfo.CreationTime and flInfo.Name without create another variable
yesterdaysList.Add(flInfo.Name.Substring(3,4));
}
基准:
我使用以下代碼進行了基准測試:
class Program {
static void Main( string[ ] args ) {
DirectoryInfo directory = new DirectoryInfo( @"D:\Films" );
Stopwatch timer = new Stopwatch( );
timer.Start( );
for ( int i = 0; i < 100000; i++ ) {
List<string> yesterdaysList = directory.GetFiles( ).Where( x => x.CreationTime.Date == DateTime.Today.AddDays( -1 ) )
.Select( x => x.Name )
.ToList( );
}
timer.Stop( );
TimeSpan elapsedtime = timer.Elapsed;
Console.WriteLine( string.Format( "{0:00}:{1:00}:{2:00}", elapsedtime.Minutes, elapsedtime.Seconds, elapsedtime.Milliseconds / 10 ) );
timer.Restart( );
DateTime yesterday = DateTime.Today.AddDays( -1 ); //initialize this variable only one time
for ( int i = 0; i < 100000; i++ ) {
List<string> yesterdaysList = new List<string>( );
foreach ( FileInfo flInfo in directory.GetFiles( ) ) {
if ( flInfo.CreationTime.Date == yesterday.Date ) //use directly flInfo.CreationTime and flInfo.Name without create another variable
yesterdaysList.Add( flInfo.Name.Substring( 3, 4 ) );
}
}
timer.Stop( );
elapsedtime = timer.Elapsed;
Console.WriteLine( string.Format("{0:00}:{1:00}:{2:00}", elapsedtime.Minutes, elapsedtime.Seconds, elapsedtime.Milliseconds / 10));
timer.Restart( );
for ( int i = 0; i < 100000; i++ ) {
List<string> list = new List<string>( );
foreach ( FileInfo flInfo in directory.GetFiles( ) ) {
DateTime _yesterday = DateTime.Today.AddDays( -1 );
String name = flInfo.Name.Substring( 3, 4 );
DateTime creationTime = flInfo.CreationTime;
if ( creationTime.Date == _yesterday.Date )
list.Add( name );
}
}
elapsedtime = timer.Elapsed;
Console.WriteLine( string.Format( "{0:00}:{1:00}:{2:00}", elapsedtime.Minutes, elapsedtime.Seconds, elapsedtime.Milliseconds / 10 ) );
}
}
結果:
First solution: 00:19:84
Second solution: 00:17:64
Third solution: 00:19:91 //Your solution
我認為您是在文件系統級別獲得更高的效率,而不是在 C# 級別。 如果是這種情況,答案是否定的:無法告訴文件系統按日期過濾。 它會不必要地返回一切。
如果您追求 CPU 效率:這是毫無意義的,因為將項目添加到列表框比按日期過濾要貴得多。 優化您的代碼不會產生任何結果。
我不想用正確的創建日期創建足夠的文件來做一個不錯的基准測試,所以我做了一個更通用的版本,它需要開始和結束時間並給出匹配的文件的名稱。 讓它給出昨天創建的文件的特定子字符串,自然而然地隨之而來。
我想出的最快的單線程純 .NET 答案是:
private static IEnumerable<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated)
{
foreach(FileInfo fi in new DirectoryInfo(directory).GetFiles())
if(fi.CreationTime >= minCreated && fi.CreationTime <= maxCreated)
yield return fi.Name;
}
我原以為EnumerateFiles()
會稍微快一點,但結果會稍微慢一點(如果你通過網絡訪問可能會更好,但我沒有測試過)。
有一點點收獲:
private static ParallelQuery<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated)
{
return new DirectoryInfo(directory).GetFiles().AsParallel()
.Where(fi => fi.CreationTime >= minCreated && fi.CreationTime <= maxCreated)
.Select(fi => fi.Name);
}
但並不多,因為它對實際調用GetFiles()
沒有幫助。 如果您沒有要使用的內核,或者GetFiles()
結果不夠大,那么它只會讓事情變得更糟( AsParallel()
的開銷大於並行過濾的好處)。 另一方面,如果您也可以並行執行后續處理步驟,則整體應用程序速度可能會提高。
用EnumerateFiles()
做這件事似乎沒有意義,因為它似乎不能很好地並行化,因為它基於我將要持續的相同方法,而且本質上是串行的 - 需要前一個結果來產生下一個。
我得到的最快的是:
public const int MAX_PATH = 260;
public const int MAX_ALTERNATE = 14;
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
public static implicit operator long(FILETIME ft)
{
return (((long)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
}
};
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct WIN32_FIND_DATA
{
public FileAttributes dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_ALTERNATE)]
public string cAlternate;
}
[DllImport("kernel32", CharSet=CharSet.Unicode)]
public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32", CharSet=CharSet.Unicode)]
public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]
public static extern bool FindClose(IntPtr hFindFile);
private static IEnumerable<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated)
{
long startFrom = minCreated.ToFileTimeUtc();
long endAt = maxCreated.ToFileTimeUtc();
WIN32_FIND_DATA findData;
IntPtr findHandle = FindFirstFile(@"\\?\" + directory + @"\*", out findData);
if(findHandle != new IntPtr(-1))
{
do
{
if(
(findData.dwFileAttributes & FileAttributes.Directory) == 0
&&
findData.ftCreationTime >= startFrom
&&
findData.ftCreationTime <= endAt
)
{
yield return findData.cFileName;
}
}
while(FindNextFile(findHandle, out findData));
FindClose(findHandle);
}
}
沒有IDisposable
承諾的FindClose()
是冒險的,並且IEnumerator<string>
的手動實現不僅應該使這更容易(這樣做的嚴重原因)而且還希望減少 3 納秒或其他東西(不是這樣做的嚴重原因),但以上顯示了基本思想。
我用 :
DirectoryInfo dI = new DirectoryInfo(fileLocation);
var files = dI.GetFiles().Where(i=>i.CreationTime>=dateFrom && i.CreationTime<=dateTo);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.