Procedura: eseguire una query per trovare il file o i file più grandi in una struttura di directory (LINQ)
Aggiornamento: novembre 2007
In questo esempio vengono illustrate cinque query relative alla dimensione dei file in byte:
Come recuperare la dimensione in byte del file più grande.
Come recuperare la dimensione in byte del file più piccolo.
Come recuperare il file più grande o più piccolo dell'oggetto FileInfo da una o più cartelle in una cartella radice specificata.
Come recuperare una sequenza, ad esempio i 10 file più grandi.
Come ordinare i file in gruppi in base alla dimensione del file in byte, ignorando i file di dimensione inferiore a un valore specificato.
Esempio
Nell'esempio seguente sono contenute cinque query distinte che illustrano come eseguire una query e raggruppare file, in base alla dimensione del file in byte. È possibile modificare facilmente questi esempi per basare la query su un'altra proprietà dell'oggetto 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;
}
}
Per restituire uno o più oggetti FileInfo completi, la query deve prima esaminare ciascun oggetto nell'origine dati e quindi ordinarli in base al valore della proprietà Length. Successivamente può restituire il singolo oggetto o la sequenza con le lunghezze maggiori. Utilizzare First per restituire il primo elemento di un elenco. Utilizzare Take<TSource> per restituire il primo numero n di elementi. Specificare un ordinamento decrescente per inserire gli elementi più piccoli all'inizio dell'elenco.
La query effettua una chiamata a un metodo separato per ottenere la dimensione del file in byte allo scopo di utilizzare la possibile eccezione generata nel caso in cui un file sia stato eliminato in un altro thread dopo la creazione dell'oggetto FileInfo nella chiamata a GetFiles. Sebbene l'oggetto FileInfo sia già stato creato, l'eccezione può verificarsi poiché un oggetto FileInfo tenta di aggiornare la proprietà Length utilizzando la dimensione in byte più recente al primo accesso alla proprietà. Inserendo questa operazione in un blocco try/catch all'esterno della query, si evita di eseguire operazioni nelle query che possono causare effetti collaterali. In generale, è necessario prestare particolare attenzione durante la gestione delle eccezioni, per assicurarsi che un'applicazione non venga lasciata in uno stato sconosciuto.
Compilazione del codice
Creare un progetto di Visual Studio destinato a .NET Framework versione 3.5. Per impostazione predefinita, il progetto contiene un riferimento a System.Core.dll e una direttiva using (C#) o un'istruzione Imports (Visual Basic) per lo spazio dei nomi System.Linq.
Copiare questo codice nel progetto.
Premere F5 per compilare ed eseguire il programma.
Premere un tasto per chiudere la finestra della console.
Programmazione efficiente
Per operazioni di query complesse sul contenuto di più tipi di documenti e file, utilizzare il motore Windows Desktop Search.