Таким образом, можно выбрать, окружать ли запросы к функциям файловой системы конструктами try-catch
, или же проверять наличие ошибок вручную. Обратите внимание: это изменяет только поведение bad_alloc
, которые могут иметь или не иметь параметр ec
.
Получаем канонические пути к файлам из относительных путей
В последнем примере мы уже приводили к каноническому виду/нормализовали пути файлов. Конечно же, класс filesystem::path
способен не только хранить и проверять пути к файлам. Это помогает легко создавать пути к файлу из строк, а также снова разбивать их на составные части.
На данном этапе класс path позволяет абстрагироваться от деталей работы операционной системы, но в некоторых случаях все же о них следует помнить.
Мы увидим, как обращаться с путями и их композицией/декомпозицией, на примере работы с абсолютными и относительными путями.
Как это делается
В данном примере мы будем работать с абсолютными и относительными путями к файлам, чтобы увидеть сильные стороны класса path
и связанных с ним вспомогательных функций.
1. Сначала включаем все необходимые заголовочные файлы и объявляем, что используем пространства имен std
и filesystem
:
#include
#include
using namespace std;
using namespace filesystem;
2. Затем объявляем пример пути к файлу. На данный момент неважно, существует ли текстовый файл, на который он ссылается. Тем не менее есть функции, генерирующие исключения, если требуемого файла нет.
int main()
{
path p {"testdir/foobar.txt"};
3. Сейчас мы познакомимся с четырьмя разными функциями библиотеки для работы с файловой системой. Функция current_path
возвращает путь, в котором в данный момент выполняется программа, — так называемый absolute
принимает относительный путь к файлу наподобие нашего пути p
и возвращает абсолютный, однозначный путь во всей файловой системе.
Функция system_complete
делает практически то же самое, что и функция absolute
в Linux, MacOS или других UNIX-подобных операционных системах. В Windows мы получим абсолютный путь к файлу, только вначале будет добавлено буквенное обозначение тома диска (например, "C:"
). Функция canonical
опять же делает то же самое, что и функция absolute
, но потом дополнительно убирает все косвенные адреса, такие как ".
" (сокращение для ..
" (сокращение для
cout << "current_path : " << current_path()
<< "\nabsolute_path : " << absolute(p)
<< "\nsystem_complete : " << system_complete(p)
<< "\ncanonical(p) : " << canonical(p)
<< '\n';
4. Еще одна приятная особенность класса path
заключается в том, что он перегружает оператор /
. Таким образом, можно сцеплять имена папок и имена файлов с помощью данного оператора и составлять из них пути файлов. Попробуем это и отобразим составной путь:
cout << path{"testdir"} / "foobar.txt" << '\n';
5. Рассмотрим работу с функцией canonical и составными путями. Передавая функции canonical
относительный путь к файлу, например "foobar.txt"
, и составной абсолютный путь current_path()/"testdir"
, получаем существующий абсолютный путь к файлу. В следующем обращении к функции передаем наш путь p
(т.е. "testdir/foobar.txt"
) и абсолютный путь current_path()
, который направляет нас в каталог "testdir"
и обратно. Это то же самое, что и current_path()
, из-за косвенных адресов. В обоих вызовах функция canonical
должна возвратить одинаковый абсолютный путь.
cout << "canonical testdir : "
<< canonical("foobar.txt",
current_path()/"testdir")
<< "\ncanonical testdir 2 : "
<< canonical(p, current_path()/"testdir/..")
<< '\n';
6. Кроме того, можно проверить эквивалентность двух путей, не являющихся каноническими. Функция equivalence
приводит к каноническому виду пути к файлам, которые она принимает в качестве аргументов, и в конечном итоге возвращает значение true
при условии, что они описывают один и тот же путь. Для этой проверки путь к файлу должен действительно существовать, в противном случае функция сгенерирует исключение.