May 11, 2020

Работа с файловой системой без WinAPI/Linux API

std::filesystem присутствует только в стандарте C++17, потому надо выставить именно этот стандарт в настройках компилятора. Если же вы по какой-то причине не можете или не хотите использовать этот стандарт, а только более старые, то вместо

#include <filesystem>

вам потребуется

#include <experimental/filesystem>

Однако, во втором случае пространство имён filesystem будет доступно только в пространстве имён std::experimental, а я буду ориентироваться на первый случай, поэтому все, кто пользуется каким-нибудь C++14, то вставьте в свой код это:

#ifdef _EXPERIMENTAL_FILESYSTEM_
namespace std
{
	using namespace experimental;
}
#endif // _EXPERIMENTAL_FILESYSTEM_

Зачем же нам нужен std::filesystem, если в Windows есть WinAPI штуки для работы с путями, как и в линуксе? Потому что так проще и код получается менее платформозависимым.

Кодировка

Windows любит wchar_t, UNIX же, напротив, обычный char. Из-за этой особенности мы для Windows будем использовать вывод в консоль через std::wcout, а в UNIX через std::cout. Мне привычнее второй вариант, но вы так же без проблем можете использовать и первый за исключением некоторых нюансов.

Класс std::filesystem::path

Итак, когда мы настроили кодировку, то можем рассмотреть класс, который мы в последствии будем часто использовать.

Создаётся объект std::filesystem::path вот так (примеры):

std::filesystem::path path1 = std::filesystem::current_path();
std::filesystem::path path2 = "New folder 1"; //так же принимает строки типа std::string и std::wstring
std::filesystem::path path3 = path2 / "Sub folder"; //"New folder/Sub folder", такой интересный синтаксис
std::filesystem::path path4 = path3.parent_path(); //path4 такой же, как и path2

Но это относительный путь, можно получить и абсолютный:

std::filesystem::path Absolute = std::filesystem::absolute(path1); //path содержит абсолютный путь до текущей директории

Есть и не менее интересные методы этого класса, но у нас и так много работы впереди, их можно рассмотреть здесь.

Работа с директориями/файлами

С прошлого раздела у нас осталась директория "New folder 1/Sub folder/", предлагаю поместить руками туда файл text.txt, теперь наш файл имеет такой относительный путь: "New folder 1/Sub folder/text.txt"

А точно ли? Можем проверить при помощи следующей функции:

bool std::filesystem::exists(const std::filesystem::path& _Target);

Пример:

if (!std::filesystem::exists("New folder 1/Sub folder/text.txt")
	std::cerr << "File not found!" << std::endl;

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

Теперь научимся создавать директории, вот код:

std::filesystem::path path("New folder 1");
	
std::filesystem::create_directory("New folder 2");
std::filesystem::create_directory(path / "Sub folder 2");

Здесь мы создаём папку "New folder 2" и "Sub folder 2" внутри "New folder 1", вот как это выглядит:

текущая директория
текущая директория/New folder 1/

Мне больше не нужна директория "New folder 2", хочу от неё избавиться, но в ней есть другая директория, то есть нужен рекурсивный обход? Верно, но здесь это реализовано:

std::filesystem::remove_all("New folder 2");

А если рекурсивный обход не нужен или я хочу удалить один лишь файл?

std::filesystem::remove("New folder 1/Sub folder 1/text.txt");

И да, я указываю строку, хотя функции принимают путь. Дело в том, что так можно.

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

std::filesystem::rename("New folder 1/Sub folder/text.txt", "document.txt");

Правда, вы не найдёте свой файл больше в Sub folder, потому что забыли указать во втором аргументе, что он там должен остаться, то есть мы его ещё и переместили, но исправим:

std::filesystem::rename("New folder 1/Sub folder/text.txt", "New folder 1/Sub folder/document.txt");

Можно даже скопировать, скопировать файл или ссылку, я просто оставлю прототипы функций здесь:

void std::filesystem::copy(const std::filesystem::path& _From, const std::filesystem::path& _To);
bool std::filesystem::copy_file(const std::filesystem::path& _From, const std::filesystem::path& _To);
void std::filesystem:copy_symlink(const std::filesystem:path& _Symlink, const std::filesystem:path& _New_symlink);

Можно обрезать/дополнять файлы, если дополняем, то прибавленный размер заполняется нулями, вот пример использования:

std::filesystem::resize_file(path / "Sub folder" / "document.txt", 128);

Напомню, что файл был до этого пустой.

Метаданные

Размер файлы вычисляется функцией, пример которой ниже:

std::filesystem::file_size("New folder 1/Sub folder/documents.txt");

А вот пример использования:

if (std::filesystem::file_size("New folder 1/Sub folder/documents.txt" != 1024)
	std::filesystem::resize_file("New folder 1/Sub folder/documents.txt", 1024);

Узнать время последней записи:

std::filesystem::last_write_time("New folder 1/Sub folder/document.txt");

Возвращает объект класса std::filesystem::file_time_type.

Изменение прав доступа осуществляется функцией std::filesystem::permissions, которая имеет три версии, однако покажу всего одну и ту на примере:

std::filesystem::permissions("New folder 1/Sub folder/document.txt", std::filesystem::perms::all);

std::filesystem::perms — перечисление:


Статья получилась короче, чем я планировал из-за того, что писал довольно долго, а выложить нужно сегодня.

Желающим почитать:

https://en.cppreference.com/w/cpp/filesystem

https://www.codingame.com/playgrounds/5659/c17-filesystem

https://docs.microsoft.com/en-us/cpp/standard-library/filesystem?view=vs-2019