Поиск на сайте
Главная Журнал Форум Wiki DRKB Страны мира

Получение списка файлов и директорий в VB 2010

Введение

Как правило разработчики стремятся заставить выполняться программы быстрее и, чтобы при этом они были проще в использовании, но иногда это закрывает одни двери и открывает другие. Как раз это и произошло с Directory.GetFiles и Directory.GetDirectories.

Для ускорения получения списков из файловой системы, старые API-методы findfirst и findnext были заменены на GetFiles и GetDirectories. Недостатком GetFiles и GetDirectories было то, что эти методы возвращали массивы и процессу приходилось ждать, пока весь массив не считается. Для устранения ожидания были созданы более продвинутые их аналоги: EnumerateFiles и EnumerateDirectories, которые позволяют рабочему процессу сразу начать работать с файлами, недожидаясь полной загрузки списка, однако и тут есть несколько незначительных препятствий.

Используя EnumerateFiles и EnumerateDirectories у вас отпадает необходимость в использовании API функций, в построении собственного рекурсивного кода для обхода всех под-директорий. Ну и как уже упоминалось выше, отпадает необходимость в ожидании получения всего списка, чтобы начать работать с результатами. Основной неприятный момент здесь в том, что если возникнет ошибка при получении списка файлов или директорий, то методы EnumerateXxx сгенерируют исключение и процесс обхода файловой системы будет прерван.

Давайте взглянем на EnumerateFiles и EnumerateDirectories, посмотрим как использовать LINQ и обработчики исключений при получении списков файлов и папок.

Использование EnumerateFiles и EnumerateDirectories

Версии EnumerateFiles и EnumerateDirectories существуют в обоих классах Directory и DirectoryInfo пространства имен System.IO. Версия в классе Directory может проходить и возвращать только подкаталоги. Для работы с файлами или каталогами можно воспользоваться версией в DirectoryInfo. Оба метода EnumerateFiles и EnumerateDirectories имеют несколько перегруженных версий, которые имеют различные аргументы включая начальный путь, маску файлов и аргумент SearchOption позволяющий задать, будет ли осуществляться поиск только в каталогах верхнего уровня или во всех директориях.

Следующий код использует вызов Directory.EnumerateFiles, возвращающий список файлов в корневом каталоге диска C:. Метод Array.ForEach преобразует результат поиска в массив и выводит результаты в консоль.

  Dim files = From file In Directory.EnumerateFiles("C:\")
              Select file
  Array.ForEach(files.ToArray(), Sub(f) Console.WriteLine(f))

Это самый простой пример использования EnumerateFiles и EnumerateDirectories - собственно то, чего мы и хотим от фреймворка.

Обработка исключений EnumerateDirectories

Если вызвать Directory.EnumerateDirectories с аргументом SearchOption.AllDirectories, то EnumerateDirectories начнёт выполнять обход директории, включая поддиректории. Однако, если начать обход корневого каталога, то есть большая вероятность, что какая-нибудь директория вызовет исключение. Как правило исключения возникают при обходе папок с пользователькими данными. Например, если написать:

  Dim d = Directory.EnumerateDirectories("C:\", "*.*", SearchOption.AllDirectories)

Тогда код будет компилироваться и работать. Если же результат источника использовать в for loop:

  'Получить все директории
  For Each d In Directory.EnumerateDirectories("C:\", "*.*", SearchOption.AllDirectories)
    Console.WriteLine(d)
  Next

то при обращении к директории C:\Documents and Settings может возникнуть исключение UnauthorizedAccessException. Чтобы поймать исключение помещаем for loop в блок Try Catch:

  Try
    For Each d In Directory.EnumerateDirectories("C:\", "*.*", SearchOption.AllDirectories)
      Console.WriteLine(d)
    Next
  Catch ex As Exception
    Console.WriteLine("Handle exception here")
  End Try

Если понадобиться отловить все возможные исключения, то можно воспользоваться классом Exception или сделать несколько блоков catch, чтобы обработать каждое исключение индивидуально. Тем не менее, чтобы получить все директории, даже если доступ запрещен, то потребуется несколько иной подход, нежели просто использование аргумента SearchOption.AllDirectories.

Поиск по всем директориям с обработкой исключений

Предположим, вы хотите найти все папки, а при возникновении исключения пропустить директорию и продолжить. Для этого можно воспользоваться аргументом SearchOption.ContinueOnErrors либо сделать раздельный вызов для каждой поддиректории, с обработчиком событий, чтобы можно было обработать исключительную ситуацию и продолжить обход следующих подпапок.

Для эффективного непрерывного обхода директорий можно использовать объект Stack. В процессе извлекая из стека корневые папки и подпапки, обрабатывая исключения и продолжая обработку подпапок, пока все директории не будут пройдены:

  Imports System.IO
  Imports System.Security
  
  Module Module1
  
      Sub Main()
        Dim results As List(Of String) = New List(Of String)
        Dim start As String = "c:\"
        results.Add(start)
        Dim stack As Stack(Of String) = New Stack(Of String)
  
        Do
          Try
            Debug.WriteLine(start)
  
            Dim dirs = From d In Directory.EnumerateDirectories(start,
              "*.*", SearchOption.TopDirectoryOnly)
              Select d
  
            ' многострочное Lambda-выражение - на самом деле здесь не обязательно
            Array.ForEach(dirs.ToArray(), Sub(d)
              stack.Push(d)
            End Sub)
  
            start = stack.Pop()
            results.Add(start)
  
          Catch ex As UnauthorizedAccessException
            Console.WriteLine(ex.Message)
            start = stack.Pop()
            results.Add(start)
          End Try
  
        Loop Until (stack.Count = 0)
  
        For Each d In results
            Console.WriteLine(d)
        Next
  
        Console.ReadLine()
  
      End Sub
  End Module

Листинг 1: Использование стека для отслеживания директорий верхнего уровня и обработки поддиректорий в случае возникновения исключения.

List (Of String) будет содержать найденные каталоги. Переменная start указывает, что поиск производится в корневом каталоге диска C:. stack используется для хранения каталогов, которые необходимо найти. Цикл Do запускает процесс, а Try содержит блок обработки исключений.

Выражение Dim dirs запускает поиск подпапок в директории указанной в start. Аргумент SearchOption.TopDirectoryOnly указывает, что искать поддиректории необходимо только в каталоге указанном в start. (параметр "*.*" можно не указывать, если дополнительная фильтрация не требуется) Array.ForEach запихивает все дочерние папки в stack, используя для этого многострочное Lambda-выражение. Далее, из stack извлекается папка и сохранятся в списке результатов. Процесс продолжается, пока стек не станет пустым. При возникновении исключения из стека вновь извлекается значение и добавляется в список результатов.

Обход файловой системы начиная с корня - довольно длительный процесс. Но поскольку код разбит на несколько этапов, то у Вас есть много возможностей взаимодействовать с результатами по ходу выполнения данного примера, кроме того можно даже запихнуть этот код в фоновый процесс как, вероятно, это делает Windows Explorer.

Резюме

EnumerateFiles и EnumerateDirectories позволяют давольно легко получать информацию о файлах и каталогах. Самая распространенная проблема для разработчиков, это то, что эти методы останавливают свою работу при возникновении исключения. Тем не менее, разделив вызовы для каждой директориии можно управлять исключениями при помощи внешнего цикла.




Основные разделы сайта


 

Реклама