Partager via


Comment : interroger les fichiers les plus volumineux dans une arborescence (LINQ)

Mise à jour : novembre 2007

Cet exemple présente cinq requêtes en rapport avec la taille des fichiers en octets :

  • Comment récupérer la taille en octets du plus grand fichier.

  • Comment récupérer la taille en octets du plus petit fichier.

  • Comment récupérer le fichier le plus grand ou le plus petit de l'objet FileInfo d'un ou de plusieurs dossiers d'un dossier racine spécifié.

  • Comment récupérer une séquence telle que les dix plus grands fichiers.

  • Comment classer des fichiers par groupes selon leur taille en octets en ignorant les fichiers qui ont une taille inférieure à la taille spécifiée.

Exemple

L'exemple suivant contient cinq requêtes distinctes qui montrent comment interroger et regrouper des fichiers selon leur taille en octets. Vous pouvez facilement modifier ces exemples pour baser la requête sur une autre propriété de l'objet 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;
    }
}

Pour retourner un ou plusieurs objets FileInfo complets, la requête doit examiner en premier chaque objet dans la source de données, puis les trier en fonction de la valeur de leur propriété Length. Elle peut ensuite retourner l'objet unique ou la séquence d'objets avec les valeurs de longueur les plus grandes. Utilisez First pour retourner le premier élément d'une liste. Utilisez Take<TSource> pour retourner le premier nombre n d'éléments. Spécifiez un tri dans l'ordre décroissant pour placer les plus petits éléments en début de liste.

La requête appelle une méthode distincte pour obtenir la taille de fichier en octets afin de consommer l'exception potentielle qui sera levée si un fichier a été supprimé sur un autre thread au cours de la période puisque l'objet FileInfo a été créé dans l'appel à GetFiles. Même si l'objet FileInfo a déjà été créé, l'exception peut se produire car un objet FileInfo essaiera d'actualiser sa propriété Length en utilisant la taille la plus actuelle en octets lors du premier accès à la propriété. En plaçant cette opération dans un bloc try-catch à l'extérieur de la requête, vous respectez la règle consistant à éviter les opérations dans les requêtes qui peuvent provoquer des effets secondaires. En général, il est recommandé d'être particulièrement vigilant lors de l'utilisation des exceptions afin de s'assurer que l'état d'une application n'est pas inconnu.

Compilation du code

  • Créez un projet Visual Studio qui cible la version 3.5 du .NET Framework. Le projet possède par défaut une référence à System.Core.dll et une directive using (C#) ou une instruction Imports (Visual Basic) pour l'espace de noms System.Linq.

  • Copiez ce code dans votre projet.

  • Appuyez sur F5 pour compiler et exécuter le programme.

  • Appuyez sur une touche pour quitter la fenêtre de console.

Programmation fiable

Pour les opérations de requête intensives sur le contenu de plusieurs types de documents et de fichiers, envisagez d'utiliser le moteur de recherche Windows Desktop Search (en anglais).

Voir aussi

Concepts

LINQ to Objects

LINQ et répertoires de fichiers