duplicate of
dupe_dir/dir1/baz.jpeg
Removed dupe_dir/dir2/feed_fish.jpg because it is a duplicate of
dupe_dir/dir1/feed_fish.jpg
Removed dupe_dir/dir2/foo.jpg because it is a duplicate of
dupe_dir/dir1/foo.jpg
Removed dupe_dir/dir2/fox.jpg because it is a duplicate of
dupe_dir/dir1/fox.jpg
Removed 6 duplicates.
$ du -sh dupe_dir
584K dupe_dir
Как это работает
Мы использовали функцию create_symlink, чтобы создать входную точку в другой файл в файловой системе. Это позволит избежать наличия дубликатов. Кроме того, можно создать жесткую ссылку с помощью функции create_hard_link. Семантически оба этих подхода похожи друг на друга, но жесткие ссылки имеют другие технические последствия, нежели мягкие. Некоторые форматы файловых систем и вовсе могут не поддерживать жесткие ссылки или, например, лишь определенное количество жестких ссылок, ссылающихся на один и тот же файл. Еще одна проблема заключается в том, что жесткие ссылки не могут указывать из одной файловой системы на другую.
Однако помимо деталей реализации существует еще один источник ошибок, проявляющийся при использовании create_symlink или create_hard_link. В следующих строках содержится ошибка. Можете ли вы ее заметить сразу?
path a {"some_dir/some_file.txt"};
path b {"other_dir/other_file.txt"};
remove(b);
create_symlink(a, b);
При выполнении этой программы ничего плохого не случится, но символьная ссылка будет нерабочей. Она указывает на "some_dir/some_file.txt", а это неверно. Проблема заключается в том, что она должна указывать либо на "/absolute/path/some_dir/some_file.txt", либо на "../some_dir/some_file.txt". Вызов create_ symlink использует корректный абсолютный путь, если мы напишем следующий код:
create_symlink(absolute(a), b);
Функция create_symlink не проверяет, корректен ли путь, на который мы создаем ссылку.
Дополнительная информация
Как можно заметить, наша функция определения хеша довольно незамысловата. Мы реализовали ее такой, чтобы пример оставался простым и не имел внешних зависимостей.
В чем заключается проблема нашей хеш-функции? На самом деле есть даже две проблемы.
1. Мы считываем в строку весь файл. Это будет иметь катастрофические последствия для файлов, которые крупнее нашей системной памяти.
2. Типаж хеш-функции hash<string>, представленный в С++, скорее всего, не поддерживает такие хеши.
При необходимости найти более качественную функцию подсчета хеша следует выбрать ту, которая работает быстро, безопасно для памяти и позволяет убедиться, что крупные файлы с разным содержимым не получат одинаковый хеш. Последнее требование, возможно, является самым важным. Если мы решим, что один файл является дубликатом другого и притом они не содержат одинаковые данные, то это может привести к потере данных после удаления одного из них.
Существуют более качественные алгоритмы хеширования, например MD5 или одна из разновидностей SHA. Чтобы получить доступ к таким функциям в нашей программе, можно использовать, скажем, криптографический API для OpenSSL.
Об авторе
Яцек Галовиц (Jacek Galowicz) получил степень магистра наук в области электротехники и вычислительной техники в Рейнско-Вестфальском техническом университете Ахена (Германия). Во время учебы он работал ассистентом, занимаясь как преподавательской, так и научной деятельностью, а также выступил соавтором нескольких научных публикаций. В то же время в качестве фрилансера Яцек создавал приложения и драйверы ядер на языках С и С++, задействованные в разных областях, таких как графическое программирование в 3D, базы данных, обмен данными по сети и моделирование физической среды. В последнее время он занимается программированием микроядерных операционных систем, предназначенных для виртуализации Intel x86 в Intel и FireEye и предъявляющих особые требования к производительности и безопасности. Яцек обожает реализации низкоуровневого ПО на современном С++ и старается обеспечить его высокую производительность, не усложняя при этом код. В последние годы он изучал чистое функциональное программирование и Haskell, что побудило его переключиться на создание обобщенного кода с помощью метапрограммирования.
Написание книги и в то же самое время основание компании — это интересный опыт, мне даже понравилось. Последнее, однако, оказалось возможно только благодаря поддержке и терпению моей замечательной подруги Виктории (Viktoria), моих коллег и всех моих друзей. Особую благодарность выражаю Арне Мерцу (Arne Mertz) за его бесценные замечания, а также Торстену Робицки (Torsten Robitzki) и Оливеру Брансу (Oliver Bruns) из пользовательской группы C++ Ганновера за обратную связь.
О рецензенте
Арне Мерц (Arne Mertz) — специалист по C++ с более чем десятилетним опытом. Он изучал физику в Гамбургском университете (Германия), а затем стал разработчиком ПО. В основном он работал с финансовыми приложениями, написанными на С++. Арне работает в компании Zu..hlke Engineering в Германии. Кроме того, он широко известен в кругах программистов благодаря своему блогу Simplify C++! (https://arne-mertz.de), посвященному написанию чистого и удобного в сопровождении кода на C++.
Примечания
1
Все это будет проделано средствами стандарта C++98, из нового в этом разделе только синтаксис.
2
Это предположение накладывает соответствующее ограничение на содержимое вводимого текста.
3
Автор забыл вставить выход из цикла при достижении конца введенного текста (это исправление есть в репозитории с кодом к книге):
if (it2 == end_it) {
break;
}
4
Цифра 1 совпадает со значением аргумента по умолчанию. Кроме того, это еще одно ограничение на вводимый текст — точка не обязательно обозначает конец. Это может быть часть многоточия, сокращение, разделитель дробной части числа и т.п.
5
Можно просто: ./sentence_length < lorem_ipsum.txt
6
Скорее всего, имеется в виду a*d. — Примеч. пер.