< "\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
при условии, что они описывают один и тот же путь. Для этой проверки путь к файлу должен действительно существовать, в противном случае функция сгенерирует исключение.
cout << "equivalence: "
<< equivalent("testdir/foobar.txt",
"testdir/../testdir/foobar.txt")
<< '\n';
}
7. Компиляция и запуск программы дадут следующий результат. Функция
current_path()
возвращается к домашнему каталогу на моем ноутбуке, поскольку я запустил приложение оттуда. К нашему относительному пути p
был добавлен префикс, состоящий из данной папки, с помощью функций absolute_path
, system_complete
и canonical
. Мы видим, что функции absolute_path
и system_complete
выдают абсолютно одинаковое описание пути файла в моей системе, потому что это Mac (на Linux будет так же). В компьютере с операционной системой Windows функция system_complete
добавит префикс "C:"
или любого другого диска, в котором расположен рабочий каталог.
$ ./canonical_filepath
current_path : "/Users/tfc"
absolute_path : "/Users/tfc/testdir/foobar.txt"
system_complete : "/Users/tfc/testdir/foobar.txt"
canonical(p) : "/Users/tfc/testdir/foobar.txt"
"testdir/foobar.txt"
canonical testdir : "/Users/tfc/testdir/foobar.txt"
canonical testdir 2 : "/Users/tfc/testdir/foobar.txt"
equivalence: 1
8. Мы не обрабатываем никаких исключений в нашей короткой программе. При удалении файла
foobar.txt
из каталога testdir
программа прекращает свою работу из-за исключения. Функция canonical
требует наличия действительного пути файла. Существует также функция weakly_canonical
, которая не предъявляет подобных требований.
$ ./canonial_filepath
current_path : "/Users/tfc"
absolute_path : "/Users/tfc/testdir/foobar.txt"
system_complete : "/Users/tfc/testdir/foobar.txt"
terminate called after throwing an instance of
'std::filesystem::v1:: cxx11::filesystem_error'
what(): filesystem error: cannot canonicalize:
No such file or directory [testdir/foobar.txt] [/Users/tfc]
Как это работает
Цель данного примера заключается в том, чтобы увидеть, как легко создавать новые пути динамически. В основном это связано с наличием в классе
path
удобного перегруженного оператора /
. Кроме того, функции файловой системы хорошо работают с абсолютными и относительными путями к файлам, а также с путями, которые содержат косвенные адреса .
и ..
.Есть довольно много функций, которые возвращают части экземпляра
path
, иногда даже преобразуя их. Не будем перечислять все существующие функции, поскольку для знакомства с ними лучше всего обратиться к справочным материалам по C++.Однако функции-члены класса
path
, возможно, стоит рассмотреть поближе. Посмотрим, каким функциям-членам класса path
соответствуют конкретные части пути к файлу. На следующей диаграмме показано, что пути файлов в Windows несколько отличаются от путей файлов в UNIX/Linux (рис. 10.1).Как видите, функции-члены класса
path
возвращаются для абсолютного пути. Для относительных путей root_path
, root_name
и root_directory
пусты. relative_ path
, соответственно, возвращает путь, только если тот уже является относительным. Составляем список всех файлов в каталоге
Конечно же, каждая операционная система, предлагающая поддержку файловой системы, также поставляется с утилитой, которая просто перечисляет все файлы внутри каталога в файловой системе. Самые простые примеры — команда
ls
в Linux, MacOS и других UNIX-подобных операционных системах. В DOS и Windows существует команда dir
. Обе команды составляют список из всех файлов в каталоге и предоставляют дополнительную информацию, такую как размер файла, разрешения и т.д.Однако переопределение такого инструмента также является хорошим примером, который позволит нам научиться выполнять обходы каталогов и файлов. Давайте просто сделаем это!
Наша собственная утилита
ls/dir
будет способна упорядочивать по имени все файлы в каталоге, их флаги разрешения доступа и отображать количество байт, которые они занимают в файловой системе.
Как это делается
В этом примере мы реализуем небольшой инструмент, который составляет список файлов в любом предоставленном пользователем каталоге. Он будет упорядочивать файлы в списке не только по имени, но и по типу, размеру и разрешениям доступа.
1. Сначала включим необходимые заголовочные файлы и объявим об использовании пространств имен
std
и filesystem
по умолчанию:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace filesystem;
2. Понадобится вспомогательная функция
file_info
. Она принимает ссылку на объект directory_entry
и извлекает из нее путь, а также объект file_status
(с помощью функции status
), который содержит тип файла и информацию о правах. Наконец, она извлекает и размер записи, если это обычный файл. Для каталогов и особых файлов мы просто возвращаем значение 0
. Вся информация упаковывается в кортеж.
static tuple
file_info(const directory_entry &entry)
{
const auto fs (status(entry));
return {entry.path(),