Мощная работа с файловой системой на C++

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

С приходом C++17, стандартная библиотека пополнилась мощным и удобным модулем std::filesystem, который предлагает современный подход к взаимодействию с файловой системой. Он предоставляет высокоуровневый API, делающий работу с файлами, каталогами, ссылками и метаданными более простой, интуитивно понятной и безопасной.

В этой статье мы погрузимся в мир std::filesystem и изучим:

• Основные понятия и классы модуля.

• Как работать с файлами и каталогами.

• Как получать информацию о файлах (размер, дата модификации, атрибуты).

• Как использовать итераторы для обхода файловой системы.

• Как создавать и удалять файлы и каталоги.

• Как работать с символьными ссылками (symlink).

• Как обрабатывать ошибки при работе с std::filesystem

Вместе мы разберёмся, почему std::filesystem является незаменимым инструментом для любого современного C++ разработчика, работающего с файловой системой.

Основные понятия и классы модуля

Модуль std::filesystem предоставляет набор классов и функций, которые позволяют работать с файловой системой на уровне файлов, каталогов, метаданных и символьных ссылок.

Ключевые классы

• std::filesystem::path: представляет путь к файлу или каталогу. Это основной класс, который используется для манипуляций с файловой системой. Он хранит информацию о пути и предоставляет методы для работы с ним:

— string-подобные методы: append, compare, empty, filename, parent_path и т.д.

— operator/: создает новый путь путем объединения текущего пути с другим путем.

— operator==: сравнивает два пути.

— operator!=: проверяет, неравны ли два пути.

• std::error_code: представляет ошибку, возникающую при работе с файловой системой.

• std::filesystem::file_status: содержит информацию о типе и разрешениях файла.

• std::filesystem::directory_entry: представляет запись в каталоге.

• std::filesystem::directory_iterator: итератор для обхода содержимого каталога.

• std::filesystem::recursive_directory_iterator: итератор для рекурсивного обхода содержимого каталога.

Ключевые функции

• std::filesystem::exists: проверяет, существует ли файл или каталог.

• std::filesystem::is_regular_file: проверяет, является ли объект файлом.

• std::filesystem::is_directory: проверяет, является ли объект каталогом.

• std::filesystem::create_directories: создает каталог и все его родительские каталоги.

• std::filesystem::remove: удаляет файл или каталог.

• std::filesystem::copy: копирует файл или каталог.

• std::filesystem::rename: переименовывает файл или каталог (в том числе может использоваться для переноса файла/каталога в другую директорию).

• std::filesystem::last_write_time: возвращает дату последней модификации файла.

• std::filesystem::file_size: возвращает размер файла.

• std::filesystem::current_path: возвращает путь к текущему рабочему каталогу (а также позволяет его изменить).

• std::filesystem::absolute: преобразует путь в абсолютный.

• std::filesystem::relative: преобразует путь в относительный.

Как работать с файлами и каталогами

Модуль std::filesystem предоставляет удобные инструменты для работы с файлами и каталогами. Рассмотрим основные операции.

Приведённые ниже примеры кода были проверены в среде разработки Visual Studio 2022 Community.

Дисклеймер

Перед тем, как мы перейдём к рассмотрению примеров кода, следует напомнить о том, что действовать с файловой системой следует аккуратно. Так, при вызове функции удаления директории, можно случайно удалить важную директорию, например если Вы указали «не тот» путь. Мы не призываем повторять описанное в статье. Настоящая статья предназначена исключительно для образовательных целей. Помните, что все действия, описанные в статье, выполняются на ваш страх и риск. Авторы не несут ответственности за любые последствия, возникающие в результате применения представленных методов и примеров кода, включая, но не ограничиваясь, потерей данных, повреждением файлов или системных настроек. Перед выполнением любых операций с файловой системой настоятельно рекомендуется создавать резервные копии важных данных и проявлять осторожность.

Создание каталога

Для создания каталога можно использовать функцию std::filesystem::create_directory либо std::filesystem::create_directories. Отличия между этими функциями в том, что вторая, в отличие от первой, в случае с вложенными каталогами, создаёт родительские каталоги (если они отсутствуют). Первая же функция, в случае отсутствия родительских каталогов, завершится ошибкой. Обработку ошибок мы рассмотрим ниже в данной статье.

Создание каталога (папки)
Создание каталога (папки)

Удаление каталога

Для удаления каталога оптимальна функция std::filesystem::remove_all. В отличие от функции std::filesystem::remove, первая удаляет также всё содержимое удаляемой папки. При попытке применить вторую функцию на непустой папке - возникнет ошибка.

Удаление каталога
Удаление каталога

Удаление файла

Для удаления файла достаточно применить функцию std::filesystem::remove. Принцип её использования аналогичен тому, что приведено выше, только вместо имени каталога применяется имя файла.

Удаление файла
Удаление файла

Проверка существования файла или каталога

Для данной цели применяется функция std::filesystem::exists

Проверка существования файла/каталога
Проверка существования файла/каталога

Получение размера файла

Для получения размера файла оптимально применять функцию std::filesystem::file_size

Получение размера файла
Получение размера файла

Получение относительного пути

Относительный путь можно получить при помощи функции std::filesystem::relative. В качестве первого параметра этой функции передаётся путь, для которого требуется найти относительный путь. Второй параметр опционален и означает путь к директории, относительно которой требуется найти искомый путь. При отсутствии этого параметра, по умолчанию принимается путь к текущему рабочему каталогу.

Получение относительного пути
Получение относительного пути

В данном примере получаем путь к файлу, относительно текущего рабочего каталога.

Итерация по каталогу

При помощи предоставляемых библиотекой итераторов можно выполнить перебор файлов и папок в каталоге с помощью цикла for.

Перебор элементов каталога
Перебор элементов каталога

Рекурсивный перебор файлов в каталоге

В отличие от предыдущего примера, можно также применять рекурсивный перебор элементов каталога (т.е. кроме файлов, лежащих в корневой директории исследуемого каталога, также будут обработаны файлы, вложенные в подкаталоги).

Рекурсивный перебор элементов каталога
Рекурсивный перебор элементов каталога

Перемещение файла или каталога

Описанный ниже код может применяться как для переименования файла, так и для его перемещения - всё зависит от указанных путей.

Переименование (перемещение) файла
Переименование (перемещение) файла

Копирование файла или каталога

Копирование файла выполняется подобным образом, но с использованием функции std::filesystem::copy.

Копирование файла
Копирование файла

Для копирования каталога требуется также передать в качестве третьего параметра функции значение std::filesystem::copy_options::recursive. Иначе, будет скопирована только внешняя папка, без содержимого.

Копирование директории со всем её содержимым
Копирование директории со всем её содержимым

Работа с символьными ссылками

std::filesystem предоставляет удобные функции для работы с символьными ссылками (symlinks) - специальными файлами, которые ссылаются на другие файлы или каталоги. Символьные ссылки также называют ярлыками.

Создание ссылки на файл

Для создания ссылки используется функция std::filesystem::create_symlink.

Создание символьной ссылки на файл
Создание символьной ссылки на файл

Дополнительные функции библиотеки std::filesystem

Выше были описаны ключевые функции библиотеки std::filesystem. Теперь обсудим дополнительные средства.

Работа с итераторами

• std::filesystem::begin: возвращает итератор, указывающий на начало (первый элемент) в директории, заданной переданным в качестве параметра итератором. Используется в сочетании с directory_iterator и recursive_directory_iterator.

• std::filesystem::end: возвращает итератор, указывающий на конец директории. Используется в сочетании с directory_iterator и recursive_directory_iterator.

Работа с путями

• std::filesystem::canonical: возвращает "канонический" путь, который является абсолютным и не содержит символических ссылок. Разрешает все символические ссылки, относительные пути, и возвращает полный путь к файлу или каталогу. Целевой объект должен существовать, иначе функция сгенерирует ошибку.

• std::filesystem::weakly_canonical: эта функция также возвращает "канонический" путь, но с некоторыми упрощениями. Она разрешает символические ссылки и относительные пути, но при этом не требует, чтобы целевой файл или каталог существовал. Если файл не существует, она просто возвращает путь без выбрасывания исключения.

• std::filesystem::proximate: позволяет получить "приблизительный" путь к файлу или каталогу относительно другого пути. Поведение похоже на std::filesystem::relative (см. выше), но в отличие от него может также учитывать символические ссылки и другие особенности файловой системы.

Работа с файлами

• std::filesystem::copy_file: копирует содержимое одного файла в другой. В отличие от std::filesystem::copy функция copy_file предназначена только для копирования содержимого файлов, в то время как copy может копировать как файлы, так и каталоги.

• std::filesystem::resize_file: изменяет размер файла до заданного размера.

• std::filesystem::space: возвращает информацию о пространстве, доступном на файловой системе, для заданного пути. Возвращает структуру std::filesystem::space_info. Ниже представлен пример использования данной функции.

Запрос информации о пространстве в файловой системе
Запрос информации о пространстве в файловой системе

Работа с символическими ссылками

• std::filesystem::copy_symlink: копирует символьную ссылку из одного места в другое.

• std::filesystem::create_directory_symlink: создает символьную ссылку на каталог.

• std::filesystem::create_hard_link: создает жесткую ссылку на файл. В отличие от символических ссылок, жесткие ссылки не указывают на файл через путь, а непосредственно ссылаются на его данные в файловой системе.

• std::filesystem::read_symlink: возвращает путь, на который ссылается символьная ссылка.

• std::filesystem::symlink_status: позволяет узнать, является ли путь символической ссылкой, а также получить информацию о целевом файле или директории, на которую ссылается эта ссылка.

Дополнительные проверки и информация

• std::filesystem::equivalent: проверяет, указывают ли два пути на один и тот же файл или каталог.

• std::filesystem::hard_link_count: возвращает количество жестких ссылок на файл.

• std::filesystem::hash_value: возвращает хеш-значение для пути.

• std::filesystem::is_block_file: проверяет, является ли файл специальным файлом блокового типа.

•std::filesystem::is_character_file: проверяет, является ли файл специальным файлом символьного типа (например, последовательный порт).

• std::filesystem::is_empty: проверяет, является ли каталог или файл пустым.

• std::filesystem::is_fifo: проверяет, является ли файл каналом FIFO (First In, First Out).

• std::filesystem::is_other: проверяет, является ли файл другим типом объекта файловой системы.

• std::filesystem::is_socket: проверяет, является ли файл сокетом.

• std::filesystem::is_symlink: проверяет, является ли файл символической ссылкой.

• std::filesystem::permissions: возвращает набор разрешений доступа к файлу.

• std::filesystem::status_known: проверяет, известен ли статус файла.

Другие полезные функции

• std::filesystem::temp_directory_path: возвращает путь к временной директории.

• std::filesystem::u8path: создает объект path из строки UTF-8.

• std::filesystem::swap: обменивает значения двух объектов path.

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

Обработка ошибок при работе с std::filesystem

Модуль std::filesystem предоставляет механизмы для обработки ошибок, которые могут возникнуть при работе с файловой системой.

Использование std::error_code

Многие из описанных выше функций std::filesystem принимают в качестве дополнительного аргумента параметр с типом std::error_code для получения информации об ошибке. Рассмотрим обработку ошибки на примере запроса размера файла.

Общий принцип следующий:

1. Инициализация std::error_code:

Создайте объект `std::error_code` для хранения информации о возникших ошибках:

std::error_code ec;

2. Вызов функции, которая может завершиться с ошибкой:

uintmax_t fileSize = std::filesystem::file_size(pathToFile, ec);

3. Проверка на ошибки:

Проверьте, содержит ли объект std::error_code информацию об ошибках:

if (ec) {
// Обработка ошибки, например, вывод сообщения об ошибке
std::cerr << "Ошибка при получении размера файла: " << ec.message() << std::endl;
}
else {
// Успешное получение размера файла
std::cout << "Размер файла: " << fileSize << " байт" << std::endl;
}

Проверка на успешность операции

Многие функции std::filesystem возвращают bool значение, которое указывает на успешность операции. Так, функция remove возвращает true в случае успешного удаления и false в противном случае. Но для получения подробностей случившегося также следует использовать передачу параметра std::error_code.

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

Некоторые функции std::filesystem могут выбрасывать исключения типа std::filesystem::filesystem_error в случае ошибки. Такое поведение встречается у функций, которым не были переданы параметры std::error_code.

Для обработки исключений используйте конструкции try...catch.

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

Важно:

  • Обработка ошибок при работе с файловой системой необходима для создания корректных и устойчивых программ.
  • Используйте error_code, проверку на успешность операции и try...catch для улавливания и обработки ошибок, чтобы предотвратить непредвиденные сбои в работе программы.
  • Рекомендуется ознакомиться с полным списком возможных кодов ошибок в документации std::filesystem.

Заключение

Модуль std::filesystem в C++17 предоставляет мощный и гибкий набор инструментов для работы с файловой системой. Он делает разработку приложений, взаимодействующих с файлами и каталогами, более простой, безопасной и эффективной.

В этой статье мы ознакомились с основными концепциями std::filesystem, изучили ключевые классы и функции, и рассмотрели примеры их использования. Мы также узнали, как эффективно обрабатывать ошибки при работе с файловой системой, используя std::error_code и механизм обработки исключений.

std::filesystem является незаменимым инструментом для любого современного C++ разработчика, который работает с файловой системой. Он позволяет создавать более надежные, устойчивые и функциональные программы.

Рекомендуем ознакомиться с полной документацией std::filesystem для получения более глубоких знаний и возможностей. Используйте std::filesystem в своих проектах для упрощения работы с файловой системой и создания лучших приложений.

🚀 Не упустите возможность узнать все секреты программирования! 🚀

✅ Ставьте лайк, если статья была полезной!

✅ Делитесь с друзьями, чтобы они тоже стали профи!

✅ Поддержите развитие сообщества донатом, чтобы мы могли создавать еще больше крутых материалов!

Спасибо за поддержку!

863 views·1 share