📚 Hub Books: Онлайн-чтение книгРазная литератураC++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+
1 ... 112 113 114 115 116 117 118 119 120 121
Перейти на страницу:
else if (is_block_file(fs))     { return 'b'; }

  else if (is_fifo(fs))           { return 'p'; }

  else if (is_socket(fs))         { return 's'; }

  else if (is_other(fs))          { return 'o'; }

  else if (is_regular_file(fs))   { return 'f'; }

  return '?';

}

4. Напишем еще одну вспомогательную функцию, rwx. Она принимает переменную perms (просто возвращающую тип класса перечисления из библиотеки для работы с файловой системой) и возвращает строку наподобие "rwxrwxrwx", которая описывает настройки прав для файла. Первая группа символов "rwx" описывает права на чтение, запись и исполнение (read, write and execution) для владельца файла. Следующая группа описывает те же права для всех пользователей, являющихся частью пользовательской группы, к которой принадлежит файл. Последняя группа символов описывает эти же права для всех остальных. Строка "rwxrwxrwx" означает, что все пользователи могут получить доступ к объекту любым способом; строка "rw-r--r--" — что только владелец файла может читать и изменять его, а все остальные — только читать. Мы просто создадим строку на основе этих значений бит за битом. Лямбда-выражение поможет выполнить повторяющуюся работу, связанную с проверкой, содержит ли переменная p типа perms конкретный бит владельца, и возвратит символ '-' или соответствующую букву:

static string rwx(perms p)

{

  auto check ([p](perms bit, char c) {

    return (p & bit) == perms::none ? '-' : c;

  });

  return {check(perms::owner_read,   'r'),

          check(perms::owner_write,  'w'),

          check(perms::owner_exec,   'x'),

          check(perms::group_read,   'r'),

          check(perms::group_write,  'w'),

          check(perms::group_exec,   'x'),

          check(perms::others_read,  'r'),

          check(perms::others_write, 'w'),

          check(perms::others_exec,  'x')};

}

5. Наконец, последняя вспомогательная функция принимает целочисленный размер файла и преобразует его в читабельный вид. Мы просто проигнорируем точку при делении чисел и округлим их до ближайшего значения кило-, мега- или гигабайт.

static string size_string(size_t size)

{

  stringstream ss;

  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<tuple<path, file_status, size_t>> items;

  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);

Правда заключается в том, что это всего лишь класс итератора, но функции std::begin и std::end предоставляют перегруженные версии данного типа. Данное обстоятельство позволяет вызвать функции begin и end для подобного итератора, и они снова будут возвращать итераторы. Это может показаться странным на первый взгляд, но делает наш класс более полезным. 

Инструмент текстового поиска в стиле grep

Большая часть операционных систем оснащена неким локальным инструментом поиска. Пользователи могут запустить его нажатием сочетания клавиш, а затем ввести имя локального файла, который хотели бы найти.

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

1 ... 112 113 114 115 116 117 118 119 120 121
Перейти на страницу:

Комментарии

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

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?