Мощная работа с файловой системой на 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 в своих проектах для упрощения работы с файловой системой и создания лучших приложений.
🚀 Не упустите возможность узнать все секреты программирования! 🚀
✅ Ставьте лайк, если статья была полезной!
✅ Делитесь с друзьями, чтобы они тоже стали профи!
✅ Поддержите развитие сообщества донатом, чтобы мы могли создавать еще больше крутых материалов!
Спасибо за поддержку!
