HOW TO:查詢目錄樹狀結構中的最大檔案或多個檔案 (LINQ)
更新:2007 年 11 月
這個範例顯示五個與檔案位元組大小相關的查詢:
如何擷取最大檔案的位元組大小。
如何擷取最小檔案的位元組大小。
如何從所指定根資料夾下的一個或多個資料夾中擷取最大或最小檔案的 FileInfo 物件。
如何擷取序列,例如最大的 10 個檔案。
如何根據檔案位元組大小將檔案排成群組,並忽略小於所指定大小的檔案。
範例
下列範例包含五個不同查詢,說明如何根據檔案位元組大小來查詢和分組檔案。您可以輕易修改這些範例,使查詢根據 FileInfo 物件的其他某個屬性。
Module QueryBySize
Sub Main()
' Change the drive\path if necessary
Dim root As String = "C:\Program Files\Microsoft Visual Studio 9.0"
Dim fileList = GetFiles(root)
' Return the size of the largest file
Dim maxSize = Aggregate aFile In fileList Into Max(GetFileLength(aFile))
'Dim maxSize = fileLengths.Max
Console.WriteLine("The length of the largest file under {0} is {1}", _
root, maxSize)
' Return the FileInfo object of the largest file
' by sorting and selecting from the beginning of the list
Dim filesByLengDesc = From file In fileList _
Let filelength = GetFileLength(file) _
Where filelength > 0 _
Order By filelength Descending _
Select file
Dim longestFile = filesByLengDesc.First
Console.WriteLine("The largest file under {0} is {1} with a length of {2} bytes", _
root, longestFile.FullName, longestFile.Length)
Dim smallestFile = filesByLengDesc.Last
Console.WriteLine("The smallest file under {0} is {1} with a length of {2} bytes", _
root, smallestFile.FullName, smallestFile.Length)
' Return the FileInfos for the 10 largest files
' Based on a previous query, but nothing is executed
' until the For Each statement below.
Dim tenLargest = From file In filesByLengDesc Take 10
Console.WriteLine("The 10 largest files under {0} are:", root)
For Each fi As System.IO.FileInfo In tenLargest
Console.WriteLine("{0}: {1} bytes", fi.FullName, fi.Length)
Next
' Group files according to their size,
' leaving out the ones under 200K
Dim sizeGroups = From file As System.IO.FileInfo In GetFiles(root) _
Where file.Length > 0 _
Let groupLength = file.Length / 100000 _
Group file By groupLength Into fileGroup = Group _
Where groupLength >= 2 _
Order By groupLength Descending
For Each group In sizeGroups
Console.WriteLine(group.groupLength + "00000")
For Each item As System.IO.FileInfo In group.fileGroup
Console.WriteLine(" {0}: {1}", item.Name, item.Length)
Next
Next
' Keep the console window open in debug mode
Console.WriteLine("Press any key to exit.")
Console.ReadKey()
End Sub
' This method is used to catch the possible exception
' that can be raised when accessing the FileInfo.Length property.
' In this particular case, it is safe to ignore the exception.
Function GetFileLength(ByVal fi As System.IO.FileInfo) As Long
Dim retval As Long
Try
retval = fi.Length
Catch ex As FileNotFoundException
' If a file is no longer present,
' just return zero bytes.
retval = 0
End Try
Return retval
End Function
' Function to retrieve a list of files. Note that this is a copy
' of the file information.
Function GetFiles(ByVal root As String) As System.Collections.Generic.IEnumerable(Of System.IO.FileInfo)
Return From file In My.Computer.FileSystem.GetFiles _
(root, FileIO.SearchOption.SearchAllSubDirectories, "*.*") _
Select New System.IO.FileInfo(file)
End Function
End Module
class QueryBySize
{
static void Main(string[] args)
{
QueryFilesBySize();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
private static void QueryFilesBySize()
{
string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\";
// Take a snapshot of the file system.
// fileList is an IEnumerable<System.IO.FileInfo>
var fileList = GetFiles(startFolder);
//Return the size of the largest file
long maxSize =
(from file in fileList
let len = GetFileLength(file)
select len)
.Max();
Console.WriteLine("The length of the largest file under {0} is {1}",
startFolder, maxSize);
// Return the FileInfo object for the largest file
// by sorting and selecting from beginning of list
System.IO.FileInfo longestFile =
(from file in fileList
let len = GetFileLength(file)
where len > 0
orderby len descending
select file)
.First();
Console.WriteLine("The largest file under {0} is {1} with a length of {2} bytes",
startFolder, longestFile.FullName, longestFile.Length);
//Return the FileInfo of the smallest file
System.IO.FileInfo smallestFile =
(from file in fileList
let len = GetFileLength(file)
where len > 0
orderby len ascending
select file).First();
Console.WriteLine("The smallest file under {0} is {1} with a length of {2} bytes",
startFolder, smallestFile.FullName, smallestFile.Length);
//Return the FileInfos for the 10 largest files
// queryTenLargest is an IEnumerable<System.IO.FileInfo>
var queryTenLargest =
(from file in fileList
let len = GetFileLength(file)
orderby len descending
select file).Take(10);
Console.WriteLine("The 10 largest files under {0} are:", startFolder);
foreach (var v in queryTenLargest)
{
Console.WriteLine("{0}: {1} bytes", v.FullName, v.Length);
}
// Group the files according to their size, leaving out
// files that are less than 200000 bytes.
var querySizeGroups =
from file in fileList
let len = GetFileLength(file)
where len > 0
group file by (len / 100000) into fileGroup
where fileGroup.Key >= 2
orderby fileGroup.Key descending
select fileGroup;
foreach (var filegroup in querySizeGroups)
{
Console.WriteLine(filegroup.Key.ToString() + "00000");
foreach (var item in filegroup)
{
Console.WriteLine("\t{0}: {1}", item.Name, item.Length);
}
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
// This method is used to swallow the possible exception
// that can be raised when accessing the FileInfo.Length property.
// In this particular case, it is safe to swallow the exception.
static long GetFileLength(System.IO.FileInfo fi)
{
long retval;
try
{
retval = fi.Length;
}
catch (System.IO.FileNotFoundException)
{
// If a file is no longer present,
// just add zero bytes to the total.
retval = 0;
}
return retval;
}
// This method assumes that the application has discovery
// permissions for all folders under the specified path.
static IEnumerable<System.IO.FileInfo> GetFiles(string path)
{
if (!System.IO.Directory.Exists(path))
throw new System.IO.DirectoryNotFoundException();
string[] fileNames = null;
List<System.IO.FileInfo> files = new List<System.IO.FileInfo>();
fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories);
foreach (string name in fileNames)
{
files.Add(new System.IO.FileInfo(name));
}
return files;
}
}
若要傳回一個或多個完整的 FileInfo 物件,查詢必須先檢查資料來源中的每個這項物件,然後根據它們的 Length 屬性進行排序。然後,就會傳回具有最大長度的單一物件或物件序列。使用 First 可以傳回清單中的第一個項目。使用 Take<TSource> 則可以傳回前 n 個項目。指定遞減排序次序,則會將最小的項目放在清單的開頭。
查詢會呼叫外面另一個取得檔案位元組大小的方法,以解決可能會因下列狀況引發的例外狀況 (Exception):自呼叫 GetFiles 而建立 FileInfo 物件以來的這段期間,有另一個執行緒刪除了檔案。即使已建立 FileInfo 物件,還是可能會發生這個例外狀況,原因是 FileInfo 物件會在它的 Length 屬性第一次受到存取時,嘗試用目前最新的位元組大小來重新整理這個屬性。藉由將這個作業放在查詢外面的 try-catch 區塊,就不會違反查詢中的作業不能造成副作用的規則。通常要解決例外狀況時需要十分小心,要確定應用程式不會處於未知狀態。
編譯程式碼
建立一個以 .NET Framework 3.5 版為目標的 Visual Studio 專案。此專案預設包含 System.Core.dll 的參考,以及 System.Linq 命名空間 (Namespace) 的 using 指示詞 (C#) 或 Imports 陳述式 (Visual Basic)。
將此程式碼複製至您的專案。
按 F5 編譯和執行程式。
按任何鍵離開主控台視窗。
穩固程式設計
如需對多種類型的文件和檔案內容執行大量查詢作業,可考慮使用 Windows 桌面搜尋引擎。