if (size >= 1000000000) {
ss << (size / 1000000000) << 'G';
} else if (size >= 1000000) {
ss << (size / 1000000) << 'M';
} else if (size >= 1000) {
ss << (size / 1000) << 'K';
} else { ss << size << 'B'; }
return ss.str();
}
6. Теперь наконец можно реализовать функцию main
. Начнем с проверки того, предоставил ли пользователь путь в командной строке. Если он этого не сделал, то просто возьмем текущий каталог "."
. Затем проверим, существует ли данный каталог. При его отсутствии мы не можем создать список файлов.
int main(int argc, char *argv[])
{
path dir {argc > 1 ? argv[1] : "."};
if (!exists(dir)) {
cout << "Path " << dir << " does not exist.\n";
return 1;
}
7. Заполним вектор кортежами, содержащими информацию о файлах, которые возвращает наша первая вспомогательная функция file_info
из объектов directory_entry
. Создадим directory_iterator
и передадим в его конструктор объект пути, созданный на предыдущем шаге. При переборе с помощью итератора для каталогов преобразуем объекты типа directory_entry
в кортежи, содержащие информацию о файлах, и вставляем их в вектор:
vector
transform(directory_iterator{dir}, {},
back_inserter(items), file_info);
8. Мы сохранили всю необходимую информацию в элементы вектора и можем просто сохранить ее с помощью написанных вспомогательных функций:
for (const auto &[path, status, size] : items) {
cout << type_char(status)
<< rwx(status.permissions()) << " "
<< setw(4) << right << size_string(size)
<< " " << path.filename().c_str()
<< '\n';
}
}
9. Компиляция и запуск проекта с путем к офлайн-версии документации C++ дадут следующий результат. Мы видим, что папка содержит только другие каталоги и простые файлы, поскольку первыми символами каждой строки являются 'd'
и 'f'
. Данные файлы имеют разные права доступа и, конечно, различаются по размеру. Обратите внимание: файлы представлены в алфавитном порядке, но мы не можем полагаться на эту особенность, поскольку это не требуется в стандарте С++17.
$ ./list ~/Documents/cpp_reference/en/cpp
drwxrwxr-x 0B algorithm
frw-r--r-- 88K algorithm.html
drwxrwxr-x 0B atomic
frw-r--r-- 35K atomic.html
drwxrwxr-x 0B chrono
frw-r--r-- 34K chrono.html
frw-r--r-- 21K comment.html
frw-r--r-- 21K comments.html
frw-r--r-- 220K compiler_support.html
drwxrwxr-x 0B concept
frw-r--r-- 67K concept.html
drwxr-xr-x 0B container
frw-r--r-- 285K container.html
drwxrwxr-x 0B error
frw-r--r-- 52K error.html
Как это работает
В этом примере мы проитерировали по файлам и для каждого из них проверили его статус и размер. Несмотря на то что все наши операции с файлами оказались довольно прямолинейными и простыми, обход каталога выглядит несколько непонятно.
Чтобы обойти каталог, мы просто создали итератор directory_iterator
и проитерировали с его помощью. Обход каталога фантастически легко совершить с помощью библиотеки filesystem
:
for (const directory_entry &e : directory_iterator{dir}) {
// сделать что-то
}
Про этот класс можно сказать лишь следующее:
□ он проверяет каждый элемент один раз;
□ порядок, в котором выполняется перебор, не определен;
□ элементы каталога .
и ..
уже отфильтрованы.
Однако можно заметить, что итератор directory_iterator
ведет себя как for
мы видели, как он используется в качестве итерабельного диапазона. В самом коде примера мы применили его как итератор:
transform(directory_iterator{dir}, {},
back_inserter(items), file_info);