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

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

Шрифт:

-
+

Интервал:

-
+
1 ... 112 113 114 115 116 117 118 119 120 121
Перейти на страницу:
при объединении в одном каталоге файлов с фотографиями от разных друзей и с разных устройств можно заметить, что расширения этих файлов различаются. Одни файлы формата JPEG имеют расширение .jpg, другие — .jpeg, а третьи — и вовсе .JPEG.

Некоторым людям нравится делать все расширения одинаковыми. Было бы полезно иметь возможность переименовать все файлы лишь одной командой. В то же время мы могли бы удалить все пробелы ' ' и заменить их, например, на '_'.

В данном примере мы реализуем такой инструмент и назовем его renamer. Он будет принимать диапазон входных шаблонов и их замен, это выглядит следующим образом:

$ renamer jpeg jpg JPEG jpg

В этом случае renamer рекурсивно проитерирует по текущему каталогу и выполнит поиск шаблонов jpeg и JPEG в именах всех файлов. Он заменит обе строки на jpg.

Как это делается

В этом примере мы реализуем инструмент, который рекурсивно просканирует все файлы внутри каталога и соотнесет их имена с заданным шаблоном. Все совпадения заменятся токенами, предоставленными пользователем, и найденные файлы будут переименованы соответствующим образом.

1. Сначала включим некоторые заголовочные файлы и объявим об использовании пространств имен std и filesystem:

#include <iostream>

#include <regex>

#include <vector>

#include <filesystem>

using namespace std;

using namespace filesystem;

2. Реализуем небольшую вспомогательную функцию, которая принимает путь к файлу в виде строки, а также диапазон пар для замены. Каждая пара для замены содержит шаблон и его замену. Проходя в цикле по диапазону, воспользуемся regex_replace, передавая ему входную строку и принимая преобразованную строку. После этого вернем полученную строку:

template <typename T>

static string replace(string s, const T &replacements)

{

  for (const auto &[pattern, repl] : replacements) {

    s = regex_replace(s, pattern, repl);

  }

  return s;

}

3. В функции main сначала проверим командную строку. Принимаем аргументы из нее попарно, поскольку нужно, чтобы шаблоны стояли рядом с их заменами. Первый элемент argv — всегда имя исполняемого файла. Это значит, что если пользователь предоставит хотя бы одну пару или больше, то argc должен быть нечетным и не меньше 3:

int main(int argc, char *argv[])

{

  if (argc < 3 || argc % 2 != 1) {

    cout << "Usage: " << argv[0]

         << " <pattern> <replacement> ...n";

    return 1;

  }

4. Как только мы убедились, что получили во входных значениях необходимые пары, заполним ими вектор:

  vector<pair<regex, string>> patterns;

  for (int i {1}; i < argc; i += 2) {

    patterns.emplace_back(argv[i], argv[i + 1]);

}

5. Теперь можно проитерировать по файловой системе. Для простоты определим в качестве каталога, по которому нужно проитерировать, текущий каталог приложения. Затем возьмем только имена файлов без остальной части пути и преобразуем их соответственно списку шаблонов и замен, собранному ранее. Возьмем копию opath, назовем ее rpath и заменим часть имени файла новой:

for (const auto &entry :

    recursive_directory_iterator{current_path()}) {

  path opath {entry.path()};

  string rname {replace(opath.filename().string(),

                patterns)};

  path rpath {opath};

  rpath.replace_filename(rname);

6. Для всех файлов, чьи имена совпали с нашими шаблонами, выведем на экран строку, сигнализирующую о том, что мы переименовали их. Если полученное в результате переименования имя файла уже существует, то продолжать работу невозможно. Просто будем пропускать такие файлы. Вместо этого мы могли бы прикреплять какое-то число к пути или что-то еще с целью разрешить пересечение имен.

    if (opath != rpath) {

      cout << opath.c_str() << " --> "

           << rpath.filename().c_str() << 'n';

      if (exists(rpath)) {

        cout << "Error: Can't rename."

                " Destination file exists.n";

      } else {

        rename(opath, rpath);

      }

    }

  }

}

7. Компиляция и запуск программы для примера каталога дадут следующий результат. Я поместил несколько картинок в формате JPEG в каталог, но задал для них разные окончания: jpg, jpeg и JPEG. Затем выполнил программу, передав ей шаблоны jpeg и JPEG и выбрав замену jpg для них обоих. В результате получил каталог с одинаковыми расширениями файлов:

$ ls

birthday_party.jpeg holiday_in_dubai.jpg holiday_in_spain.jpg

trip_to_new_york.JPEG

$ ../renamer jpeg jpg JPEG jpg

/Users/tfc/pictures/birthday_party.jpeg --> birthday_party.jpg

/Users/tfc/pictures/trip_to_new_york.JPEG --> trip_to_new_york.jpg

$ ls

birthday_party.jpg holiday_in_dubai.jpg holiday_in_spain.jpg

trip_to_new_york.jpg 

Создаем индикатор эксплуатации диска

Мы уже реализовали инструмент, который работает как ls в Linux/MacOS или dir в Windows, но, подобно этим утилитам, не выводит размер файлов в каталогах.

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

В данном примере мы реализуем инструмент, который делает именно это. Инструмент можно запустить для любого каталога, он определит размер всех его записей.

Как это делается

В этом примере мы реализуем приложение, которое итерирует по каталогу и перечисляет размеры файлов для каждой записи. Это просто для обычных файлов, но если мы смотрим на запись каталога, которая сама по себе является каталогом, то нужно заглянуть в него и суммировать размеры всех файлов, хранящихся в нем.

1. Сначала включим все необходимые заголовочные файлы и объявим об использовании пространств имен std и filesystem:

#include <iostream>

#include <sstream>

#include <iomanip>

#include <numeric>

#include <filesystem>

using namespace std;

using namespace filesystem;

2. Затем реализуем вспомогательную функцию, которая принимает в качестве аргумента directory_entry и возвращает его размер в файловой системе. Это не каталог, мы просто вернем размер файла, вычисленный с помощью file_size:

static size_t entry_size(const directory_entry &entry)

{

  if (!is_directory(entry)) { return file_size(entry); }

3. Если это каталог, то нужно проитерировать по всем его записям и подсчитать их размер. Мы будем вызывать вспомогательную функцию entry_size рекурсивно при повторной встрече с подкаталогами:

  return accumulate(directory_iterator{entry}, {}, 0u,

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

Комментарии

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

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