C++17 STL Стандартная библиотека шаблонов — страница 113 из 119

< "\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(),