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

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

Шрифт:

-
+

Интервал:

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

  for (const auto &[ext, stats] : ext_stats(dir)) {

    const auto &[accum_size, count] = stats;

    cout << setw(15) << left << ext << ": "

         << setw(4) << right << count

         << " items, avg size "

         << setw(4) << size_string(accum_size / count)

         << 'n';

  }

}

10. Компиляция и запуск программы дадут следующий результат. Я предоставил ей в качестве аргумента командной строки каталог, содержащий офлайн-справку по С++.

$ ./file_type ~/Documents/cpp_reference/

.css   :    2 items, avg size  41K

.gif   :    7 items, avg size 902B

.html  : 4355 items, avg size  38K

.js    :    3 items, avg size   4K

.php   :    1 items, avg size 739B

.png   :   34 items, avg size   2K

.svg   :   53 items, avg size   6K

.ttf   :    2 items, avg size 421K 

Инструмент для уменьшения размера папки путем замены дубликатов символьными ссылками

Существует множество инструментов, сжимающих данные разными способами. Наиболее известными примерами таких алгоритмов/форматов являются ZIP и RAR. Подобные инструменты уменьшают размер файлов, снижая их внутреннюю избыточность.

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

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

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

 

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

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

#include <iostream>

#include <fstream>

#include <unordered_map>

#include <filesystem>

using namespace std;

using namespace filesystem;

2. Чтобы определить, какие файлы являются дубликатами друг друга, создадим ассоциативный массив, в котором соотносятся хеши файлов и путь к первому файлу, из которого был получен этот хеш. Для получения таких хешей следует использовать популярный алгоритм, такой как MD5 или SHA. В целях сохранения данного примера чистым и простым просто считаем весь файл в строку, а затем задействуем объект хеш-функции, уже применяемый unordered_map для подсчета хешей строк:

static size_t hash_from_path(const path &p)

{

  ifstream is {p.c_str(),

               ios::in | ios::binary};

  if (!is) { throw errno; }

  string s;

  is.seekg(0, ios::end);

  s.reserve(is.tellg());

  is.seekg(0, ios::beg);

  s.assign(istreambuf_iterator<char>{is}, {});

  return hash<string>{}(s);

}

3. Затем реализуем функцию, которая создает такой ассоциативный массив, основанный на хешах, и удаляет дубликаты. Она рекурсивно итерирует по каталогу и его подкаталогам:

static size_t reduce_dupes(const path &dir)

{

  unordered_map<size_t, path> m; size_t count {0};

  for (const auto &entry :

     recursive_directory_iterator{dir}) {

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

    const path p {entry.path()};

    if (is_directory(p)) { continue; }

    const auto &[it, success] =

      m.try_emplace(hash_from_path(p), p);

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

    if (!success) {

      cout << "Removed " << p.c_str()

           << " because it is a duplicate of "

           << it->second.c_str() << 'n';

      remove(p);

      create_symlink(absolute(it->second), p);

      ++count;

    }

6. После перебора в файловой системе возвращаем количество файлов, которые мы удалили и заменили файловыми ссылками.

  }

  return count;

}

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

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

{

  if (argc != 2) {

    cout << "Usage: " << argv[0] << " <path>n";

    return 1;

  }

  path dir {argv[1]};

  if (!exists(dir)) {

    cout << "Path " << dir << " does not exist.n";

    return 1;

  }

8. Единственное, что нам осталось сделать, — вызвать функцию reduce_dupes для этого каталога и вывести на экран информацию о том, сколько файлов мы удалили:

  const size_t dupes {reduce_dupes(dir)};

  cout << "Removed " << dupes << " duplicates.n";

}

9. Компиляция и запуск программу, для примера каталога, содержащего дубликаты, выглядит следующим образом. Я использовал инструмент du, чтобы проверить размер каталога до и после запуска нашей программы, с целью продемонстрировать работоспособность нашего подхода:

$ du -sh dupe_dir

1.1M dupe_dir

$ ./dupe_compress dupe_dir

Removed dupe_dir/dir2/bar.jpg because it is a duplicate of

dupe_dir/dir1/bar.jpg

Removed dupe_dir/dir2/base10.png because it is a duplicate of

dupe_dir/dir1/base10.png

Removed dupe_dir/dir2/baz.jpeg because it is a

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

Комментарии

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

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