<< " - " << content << '\n';
}
}
}
8. Подготовим файл с именем "foobar.txt"
, содержащий тестовые строки, по которым можно выполнить поиск:
foo
bar
baz
9. Компиляция и запуск программы дадут следующий результат. Я запустил приложение в каталоге /Users/tfc/testdir
моего ноутбука и сначала передал ему шаблон "bar"
. Внутри этого каталога приложение нашло вторую строку в нашем файле "foobar.txt"
и другом файле "text1.txt"
, который находится в каталоге testdir/dir1
:
$ ./grepper bar
/Users/tfc/testdir/dir1/text1.txt:1 - foo bar bla blubb
/Users/tfc/testdir/foobar.txt:2 - bar
10. При повторном запуске приложения с шаблоном "baz"
оно находит третью строку в нашем примере текстового файла:
$ ./grepper baz
/Users/tfc/testdir/foobar.txt:3 - baz
Как это работает
Создание и использование регулярного выражения с целью фильтрации содержимого файлов — основная цель данного примера. Однако рассмотрим итератор recursive_directory_iterator
, поскольку этот особый класс итераторов мы применяли для фильтрации файлов, по которым итерируем рекурсивно.
Как и directory_iterator
, recursive_directory_iterator
итерирует по элементам каталога. Он делает это рекурсивно, согласно своему названию. При встрече с элементом файловой системы, который является directory_entry
для данного пути, а затем зайдет в него, чтобы проитерировать по его потомкам.
Итератор recursive_directory_iterator
имеет несколько интересных функций-членов.
□ depth()
— говорит, на сколько уровней итератор спустился в подкаталоге.
□ recursion_pending()
— сообщает, будет ли итератор спускаться дальше после элемента, на который он указывает в данный момент.
□ disable_recursion_pending()
— эту функцию можно вызвать, чтобы помешать итератору спуститься в следующий подкаталог, если сейчас он указывает на каталог, в который можно спуститься. Это значит, что вызов указанного метода ничего не даст, если мы совершим данное действие
□ pop()
— эта функция прерывает работу на текущем уровне и поднимает итератор на один уровень вверх в иерархии каталогов для продолжения работы.
Дополнительная информация
Еще одной важной деталью, о которой нужно знать, выступает класс-перечисление directory_options
. Конструктор класса recursive_directory_iterator
принимает значение этого типа в качестве второго аргумента. Значением по умолчанию, которое мы использовали неявно, является directory_options::none
. Другие его значения выглядят следующим образом:
□ follow_directory_symlink
— позволяет рекурсивному итератору следовать по символьным ссылкам на каталоги;
□ skip_permission_denied
— указывает итератору пропускать каталоги, которые в противном случае вернут ошибку, поскольку файловая система не дает прав на доступ к ним.
Эти настройки можно объединять с помощью оператора |
.
Инструмент для автоматического переименования файлов
На создание этого примера меня сподвигла ситуация, в которую я попадаю довольно часто. Скажем, при объединении в одном каталоге файлов с фотографиями от разных друзей и с разных устройств можно заметить, что расширения этих файлов различаются. Одни файлы формата JPEG имеют расширение .jpg
, другие — .jpeg
, а третьи — и вовсе .JPEG
.
Некоторым людям нравится делать все расширения одинаковыми. Было бы полезно иметь возможность переименовать все файлы лишь одной командой. В то же время мы могли бы удалить все пробелы ' '
и заменить их, например, на '_'
.
В данном примере мы реализуем такой инструмент и назовем его renamer
. Он будет принимать диапазон входных шаблонов и их замен, это выглядит следующим образом:
$ renamer jpeg jpg JPEG jpg
В этом случае renamer
рекурсивно проитерирует по текущему каталогу и выполнит поиск шаблонов jpeg
и JPEG
в именах всех файлов. Он заменит обе строки на jpg
.
Как это делается
В этом примере мы реализуем инструмент, который рекурсивно просканирует все файлы внутри каталога и соотнесет их имена с заданным шаблоном. Все совпадения заменятся токенами, предоставленными пользователем, и найденные файлы будут переименованы соответствующим образом.
1. Сначала включим некоторые заголовочные файлы и объявим об использовании пространств имен std
и filesystem
:
#include
#include
#include
#include
using namespace std;
using namespace filesystem;