.ttf : 2 items, avg size 421K
Инструмент для уменьшения размера папки путем замены дубликатов символьными ссылками
Существует множество инструментов, сжимающих данные разными способами. Наиболее известными примерами таких алгоритмов/форматов являются ZIP и RAR. Подобные инструменты уменьшают размер файлов, снижая их внутреннюю избыточность.
Прежде чем сжать файлы в архив, можно довольно просто снизить использование диска, просто
Как это делается
В данном примере мы реализуем небольшой инструмент, который определяет, какие файлы в каталоге дублируют друг друга. Зная это, он удалит все файлы, кроме одного, и заменит их символьными ссылками, что уменьшит размер каталога.
1. Сначала включим все необходимые заголовочные файлы и объявим об использовании пространств имен std
и filesystem
по умолчанию:
#include
#include
#include
#include
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
return hash
}
3. Затем реализуем функцию, которая создает такой ассоциативный массив, основанный на хешах, и удаляет дубликаты. Она рекурсивно итерирует по каталогу и его подкаталогам:
static size_t reduce_dupes(const path &dir)
{
unordered_map
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) {