Рассмотрим следующий пример:
FindFirst— открывает поиск. В качестве первого параметра выступает маска поиска. Если вы укажете конкретный файл, то система найдет именно его. Вы также можете искать и целые группы файлов. Например, можно запустить поиск всех файлов в корневом каталоге диска С:. Для этого первый параметр должен быть определен как— 'с: \ *. *'. Для поиска только ЕХЕ-файлов, в папке Fold вы должны указать — 'с: \Fold\ * . ехе'.
Второй параметр — атрибуты, используемые при поиске файлов. Чтобы искать любые файлы, нужно указать faAnyFiie. Помимо этого, можно искать по следующим атрибутам:
- faReadOnly — искать файлы с атрибутом Readonly (только для чтения);
- faHidden — искать скрытые файлы;
- faSysFile — искать системные файлы;
- faArchive — искать архивные файлы;
- faDirectory — искать папки.
Последний параметр— это структура, в которой нам вернется информация о поиске, а именно: имя найденного файла, размер, время создания и т. д.
После вызова этой процедуры, мы должны проверить на корректность найденный файл. Если результат равен invalid_handle_value, то функция не нашла ни одного файла. Если все нормально и файл, удовлетворяющий критериям поиска, существует, то запускается цикл Repeat.. .until.
Мы уже рассматривали циклы, но все же повторимся и вспомним их работу. Цикл выполняет операторы, расположенные между repeat и until, пока условие, расположенное после слова until, выполняется. Как только условие нарушается, цикл прерывается.
Здесь надо заметить, что функция поиска возвращает через параметр Информация имена файлов и может вернуть нам в качестве имени точку или две точки. Если вы посмотрите на папку, то таких файлов не будет. Откуда берутся эти имена? Имя файла в виде точки указывает на текущую папку, а имя файла из двух точек указывает на папку верхнего уровня. Если мы встречаем такие имена, то их нужно просто отбрасывать.
Параметр Информация имеет тип структуры TSearchRec. Давайте рассмотрим ее подробнее. Объявление выглядит так:
Функция FindNext заставляет найти следующий файл, удовлетворяющий параметрам, указанным в функции FindFirst. Этой функции нужно передать структуру searchRec, по которой будет определено, на каком месте сейчас остановлен поиск, и с этого момента он будет продолжен. Как только будет найден новый файл, функция вернет в структуре SearchRec информацию о новом найденном файле.
Функция Findciose закрывает поиск. В качестве единственного параметра нужно указать все ту же структуру SearchRec.
Давайте напишем какой-нибудь реальный пример, который наглядно покажет работу с функциями поиска файлов. Посмотрите на структуру TSearchRec. Как видите, она умеет возвращать размер найденного файла. Вот и тема для примера — мы напишем код, который будет определять размер указанного файла.
Создайте новый проект и установите на форму два компонента TEdit и одну кнопку. Можете еще украсить все это текстом. У вас должно получиться нечто похожее на рис. 10.4.
По нажатии кнопки (событие onclick) напишите следующий код:
Усложним пример и попробуем написать программу, которая будет искать файл на диске, при этом сканировать и вложенные папки. Эту задачу очень удобно решать через рекурсию. Когда мы рассматривали эту тему, то реализовали абсолютно бесполезный пример, который лучше решать через простой цикл. В данном случае вы увидите реальную мощь рекурсии, закрепите знание функций поиска и увидите неплохой алгоритм.
Итак, для примера нам понадобятся на форме два поля ввода с именами:
- edLookFor — в нем будут вводить имя файла, который нужно найти;
- edLookin — диск или путь к папке, где нужно искать, включая вложенные папки. Нам также понадобятся кнопка, по нажатии которой будет происходить сканирование, и поле ввода Memo, куда будет выводиться результат, ведь файл с одним и тем же именем может быть в нескольких папках.
По нажатии кнопки пишем следующий код:
Memo1.Lines.Clear; ScanFolder(edLookin.Text);
В первой строке очищаем поле Memo от предыдущих результатов. После этого запускается функция ScanFolder, которой передаем путь, где искать файл. Код этой функции можно увидеть в листинге 10.18, а объявление ее должно быть в разделе private вашей формы в следующем виде:
В первой же строке запускаем поиск с помощью функции FindFirst. В качестве первого параметра передается папка плюс маска Vs.*'. Второй параметр равен faAnyFile, чтобы функция искала для нас все файлы любого типа. Последний параметр — это структура TSearchRec, через которую мы будем получать результат. Если файл найден, то проверяем, если имя равно точке или двум точкам, то продолжаем поиск дальше.
После этого в переменную FileName помещаем полный путь найденного файла. Для этого используем функцию SlashSep. Эта функция не существует в Delphi, мы должны сами ее написать:
Функция получает в качестве параметров путь и имя файла. Смысл заключается в том, что если путь не заканчивается слешем, то его нужно добавить. Именно это здесь и делается.
Вернемся к листингу 10.18. На следующей стадии идет проверка, что именно мы нашли — файл или папку. Если это папка, то нужно поискать файл внутри нее. Для этого мы вызываем ScanFolder, указывая вложенную папку. Но мы же уже находимся в этой функции, получается, что функция будет вызывать сама себя? Правильно — это и есть рекурсия. Мы снова вызываем ScanFolder с указанием вложенной папки, и этот вызов будет продолжаться, пока все папки не будут просканированы.
Если мы нашли не папку, то проверяем имя файла. Если оно совпадает с искомым, то выводим соответствующее сообщение в Memo компонент. Итак, поиск продолжается до тех пор, пока не переберем все файлы.
Этот пример хорош, но не эффективен. Дело в том, что мы будем перебирать абсолютно все файлы в каждой папке, что не эффективно. Намного быстрее будет искать конкретный файл сразу. Для этого первую строку изменяем следующим образом:
if FindFirst(SlashSep(Folder, sr.Name), faAnyFile, sr) = 0 then
Вот теперь в качестве первого параметра мы указываем не просто путь с маской, а конкретный файл, и результат будет положительным только в том случае, когда в искомой папке есть нужный файл.
В качестве строки поиска можно указывать и маски, например, следующая строка ищет все INI-файлы в корне диска С:
if FindFirst(' с:*.ini', faAnyFile, sr) = 0 then
Но когда вы указываете конкретный файл, то функция не будет возвращать папки. Как решить эту проблему? Попробуйте подумать сами. Есть достаточно элегантное решение.