Рекурсия папок с помощью C#


Некоторые приложения должны считывать структуру папок в существующей директории или для всего диска или подключенного устройства. Стандартный метод в классе Directory может вызвать проблемы при использовании таким образом. Альтернативой является рекурсивное чтение папок.

Чтение папок

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

Класс каталога, который является стандартным типом в .NET framework включает в себя метод GetDirectories, который позволяет вам считывать папки по заданному пути, возвращая их имена в строковом массиве. Вы можете использовать дополнительные методы классов каталогов и файлов для работы с папками и их содержимым. Пример метода GetDirectories показан ниже. Он возвращает список имен папок, которые можно найти в корневом каталоге диска C:.

Примечание: для запуска этого кода вам необходимо обратиться к пространству имен System.IO, поэтому добавьте директиву "using System.IO;” к вашему коду.

string[] folders = Directory.GetDirectories(@"c:\");
Чтобы указать методу работать рекурсивно, читая папки, их подпапки и каталоги под ними до тех пор, пока структура папок не будет исчерпана, вы можете добавить опцию поиска по всем каталогам, как показано ниже:

string[] folders = Directory.GetDirectories(@"c:\", "*", SearchOption.AllDirectories);
К сожалению, с этим есть проблемы. Основной является то, что некоторые папки, которые вы пытаетесь прочитать, могут быть настроены таким образом, чтобы текущий пользователь не мог получить к ним доступ. Вместо того, чтобы игнорировать папки, к которым у вас ограниченный доступ, метод выдает исключение UnauthorizedAccessException. Однако мы можем обойти эту проблему, создав наш собственный код рекурсивного поиска по папкам.

Рекурсия папок

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

Приведенный ниже код показывает базовый рекурсивный метод. Основной метод вызывает ShowAllFoldersUnder, передавая начальный путь. В этом случае мы собираемся отобразить все папки, начиная с корня диска C:.

ShowAllFoldersUnder выполняет реальную работу. Когда этот метод вызывается, он использует базовую версию GetDirectories для получения списка папок для каталогов непосредственно по указанному пути. Затем он перебирает эти папки, сначала отображая имя, затем вызывая себя, передавая имя папки, чтобы заглянуть на один уровень глубже. Этот рекурсивный характер приводит к проверке всех папок, если только не возникнет исключение.

static void Main(string[] args)
{
    Console.WriteLine(@"c:\");
    ShowAllFoldersUnder(@"c:\", 0);
}
 
private static void ShowAllFoldersUnder(string path, int indent)
{
    foreach (string folder in Directory.GetDirectories(path))
    {
        Console.WriteLine("{0}{1}", new string(' ', indent), Path.GetFileName(folder));
        ShowAllFoldersUnder(folder, indent + 2);
    }
}
Чтобы избежать исключений UnauthorizedAccessExceptions, останавливающих выполнение, нам нужно добавить блок try / catch вокруг цикла foreach. Мы можем использовать это, чтобы перехватывать только этот тип исключения и игнорировать его. Это означает, что папки, к которым у пользователя нет доступа, отображаться не будут. Обновленный код " ShowAllFoldersUnder " выглядит следующим образом.

private static void ShowAllFoldersUnder(string path, int indent)
{
    try
    {
        foreach (string folder in Directory.GetDirectories(path))
        {
            Console.WriteLine("{0}{1}", new string(' ', indent), Path.GetFileName(folder));
            ShowAllFoldersUnder(folder, indent + 2);
        }
    }
    catch (UnauthorizedAccessException) { }
}
Точки повторной обработки

Кое-что еще, с чем мы должны иметь дело, - это точки повторной обработки, или точки соединения. Большинство дисков, используемых операционными системами Windows, отформатированы с использованием файловой системы новой технологии, или NTFS. Это позволяет монтировать диски интересными способами. В дополнение к обычному назначению букв дисков, таких как C:, диски могут монтироваться внутри папок. Это означает, что когда вы используете проводник Windows для перехода к папке C:\OtherDisk то, возможно, вы видите содержимое другого диска или раздела в целом.

Точки соединения создают проблему при рекурсивном сканировании папок; диски могут быть смонтированы таким образом, чтобы создавать бесконечные структуры каталогов. Например, диск, содержащий ваш диск C:, может быть смонтирован в папке с именем "C:\Infinite ". Если бы вы заглянули в эту папку, вы бы нашли все, что находится в корне C:, включая папку "Infinite". Вы можете продолжить детализацию этой папки, каждый раз видя одни и те же подпапки и файлы.

Чтобы избежать бесконечных циклов, нам нужно проверить, что папка не является точкой соединения. Мы можем сделать это, прочитав атрибуты каждой папки и проверив, является ли она точкой повторного анализа. Для получения атрибутов мы используем метод File.GetAttributes, который возвращает перечисление, представляющее битовое поле. Затем мы можем использовать побитовый оператор AND для проверки наличия флага повторной точки.

Используя эту информацию, мы можем создать окончательную версию ShowAllFoldersUnder ниже, следующим образом:

private static void ShowAllFoldersUnder(string path, int indent)
{
    try
    {
        if ((File.GetAttributes(path) & FileAttributes.ReparsePoint)
            != FileAttributes.ReparsePoint)
        {
            foreach (string folder in Directory.GetDirectories(path))
            {
                Console.WriteLine(
                    "{0}{1}", new string(' ', indent), Path.GetFileName(folder));
                ShowAllFoldersUnder(folder, indent + 2);
            }
        }
    }
    catch (UnauthorizedAccessException) { }
}
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегизаметки, си шарп, файл, файловая система




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




Файлы и потоки в C#: общая информация
Разное меню на разных страницах в WordPress
Урок 25. Работа с DateTime C#