просмотр заданного каталога, автоматически отсеивая имена файлов и папок, не удовлетворяющи х маске. Функция возвращает дескриптор (THandle), используемый для идентификации начатого просмотра папки при продолжении поиска (в функции FindNextFile).
После окончания просмотра папки вызывается функция FindClose, завершающая просмотр папки. Очень напоминает работу с обычным файлом (открытие, просмотр, закрытие), не так ли?
Листинг 4.23.
Поиск в заданной папке
function SearchInFolder(folder, mask: String; flags: DWORD;
names: TStrings; addpath: Boolean = False): Boolean;
var
hSearch: THandle;
FindData: WIN32_FIND_DATA;
strSearchPath: String;
bRes: Boolean; //Если равен True, то нашли хотя бы один
//файл или каталог
begin
strSearchPath := folder + '\' + mask;
bRes := False;
//Начинаем поиск
hSearch := FindFirstFile(PAnsiChar (strSearchPath), FindData);
if (hSearch <> INVALID_HANDLE_VALUE) then
begin
//Ищем все похожие элементы (информация о первом элементе
//уже записана в FindData функцией FindFirstFile)
repeat
if (String(FindData.cFileName) <> '.. ') and
(String(FindData.cFileName) <> '.') then
//Пропускаем . и ..
begin
if MatchAttrs(flags, FindData.dwFileAttributes) then
begin
//Нашли подходящий объект
if addpath then
names.Add(folder + '\' + FindData.cFileName)
else
names.Add(FindData.cFileName);
bRes := True;
end;
end;
until FindNextFile(hSearch, FindData) = False;
//Заканчиваем поиск
FindClose(hSearch);
end;
SearchInFolder := bRes;
end;
Результатом работы функции SearchlnFolder является заполнение списка names именами или, если значение параметра addpath равно True, полными путями найденных файлов и каталогов. Значение параметра flags (битовая маска атрибутов) формируется так же, как для функции SetFileAttributes. Только одновременно можно установить любые интересующие программиста атрибуты. При нахождении хотя бы одного файла или каталога SearchlnFolder возвращает значение True.
В функции поиска проверка соответствия атрибутов найденных файлов и каталогов производится при помощи дополнительной функции MatchAttrs. Код этой функции приведен в листинге 4.24.
Листинг 4.24.
Фильтр атрибутов
function MatchAttrs(flags, attrs: DWORD): Boolean;
begin
MatchAttrs := (flags and attrs) = flags;
end;
Может показаться, что проверка из одной строки – слишком слабый аргумент для создания отдельной функции. В рассматриваемом примере отдельная функция MatchAttrs выделена для того, чтобы сделать отсеивание файлов (и папок) по атрибутам более очевидным.
В листинге 4.24 приводится реализация нестрогого фильтра: он принимает файл или папку, если они имеют все установленные в flags атрибуты, независимо от наличия файла или папки дополнительных атрибутов. Так, если мы задали flags:= FILE_ATTRIBUTE_READONLY, то будут найдены как файлы, так и каталоги, а также скрытые, системные и прочие файлы, также имеющие атрибут FILE_ATTRIBUTE_READONLY. Для реализации строгого фильтра можно заменить выражение в функции MatchAttrs простым равенством: flags = attrs.
Возможный результат поиска с использованием функции SearchlnFolder приводится на рис. 4.5.
Рис. 4.5. Поиск в заданной папке
Пример вызова функции SearchlnFolder (для показанного на рис. 4.5 приложения) приведен в листинге 4.25.
Листинг 4.25.
Использование функции SearchlnFolder
//Запуск поиска файла в заданной папке
procedure TForm2.Button1Click(Sender: TObject);
var
flags: DWORD;
begin
//Формируем набор атрибутов (по установленным флажкам на форме)
flags := 0;
if (chkDirs.Checked) then flags := flags or FILE_ATTRIBUTE_DIRECTORY;
if (chkHidden.Checked) then flags := flags or FILE_ATTRIBUTE_HIDDEN;
if (chkSystem.Checked) then flags := flags or FILE_ATTRIBUTE_SYSTEM;
if (chkReadOnly.Checked) then flags := flags or FILE_ATTRIBUTE_READONLY;
if (chkArchive.Checked) then flags := flags or
FILE_ATTRIBUTE_ARCHIVE;
lblFound.Caption := 'Поиск…
lstFiles.Clear;
Refresh;
//Поиск (файлы записываются прямо в список на форме)
if not SearchInFolder(txtFolder.Text, txtMask.Text, flags,
lstFiles.Items)
then
lblFound.Caption := 'Поиск не дал результатов'
else
lblFound.Caption := 'Найдено объектов: ' +
IntToStr (lstFiles.Count);
end;
Поиск по всему дереву каталогов
В листинге 4.26 приводится одна из возможных реализаций рекурсивного поиска по дереву каталогов. Алгоритм поиска работает следующим образом.
1. Выполняется поиск в папке folder (все найденные файлы или папки добавляются в список names).
2. Функция SearchlnTree вызывается для каждого подкаталога BfoLder для продолжения поиска в поддереве, определяемом подкаталогом.
Листинг 4.26.
Поиск по дереву каталогов
function SearchInTree(folder, mask: String; flags: DWORD;
names: TStrings; addpath: Boolean = False): Boolean;
var
hSearch: THandle;
FindData: WIN32_FIND_DATA;
bRes: Boolean; //Если равен True, то нашли хотя бы один файл или каталог
begin
//Осуществляем поиск в текущей папке
bRes := SearchInFolder(folder, mask, flags, names, addpath);
//Продолжим поиск в каждом из подкаталогов
hSearch := FindFirstFile(PAnsiChar(folder + '*'), FindData);
if (hSearch <> INVALID_HANDLE_VALUE) then
begin
repeat
if (String(FindData.cFileName) <> '..') and
(String(FindData.cFileName) <> '.') then
//Пропускаем . и ..
begin
if (FindData.dwFileAttributes and
FILE_ATTRIBUTE_DIRECTORY <> 0)
then
//Нашли подкаталог – выполним в нем поиск
if SearchInTree(folder + '\' + String(FindData.cFileName),
mask, flags, names, addpath)
then
bRes := True;
end;
until FindNextFile(hSearch, FindData) = False;
FindClose (hSearch);
end;
SearchInTree := bRes;
end;
В функции SearchlnTree не используется просмотр каталога folder вручную (при помощи API-функций) из соображений эффективности. Если захотите, можете реализовать поиск подкаталогов при помощи функции SearchlnFolder. Правда, при этом нужно будет завести дополнительный список (TStringList) для сохранения найденных в текущем каталоге подкаталогов. Элементы списка будут использоваться только один раз: для поиска в подкаталогах.
Возможный результат поиска с использованием функции SearchlnTree приводится на рис. 4.6.
Рис. 4.6. Поиск по дереву каталогов
С небольшими модификациями алгоритм рекурсивного обхода дерева каталогов, реализованный в листинге 4.25, можно использовать и при операциях, отличных от простого поиска: например, при копировании или удалении дерева каталогов. Для этого достаточно выполнять нужные операции над каждым найденным объектом.
Построение дерева каталогов
Рассмотрим довольно интересный пример, основанный на использовании функции поиска SearchlnFolder, – построение дерева каталогов для определенного диска (рис. 4.7).
Рис. 4.7. Дерево каталогов
Для простоты (и чтобы не отвлекать внимания от построения дерева) диск задается в программе жестко. При необходимости это можно легко исправить (как определять диски, вы уже знаете).
Рассмотрим работу приложения по порядку. Элемент управления TreeView на форме имеет имя tree. Содержимое списка изображений (ImageList), используемого деревом, приведено на рис. 4.8.
Рис. 4.8. Изображения для элементов дереваПервый элемент дерева (соответствует диску) образуется при создании формы (листинг 4.27).
Листинг 4.27.
Создание первого элемента дерева – диска
procedure TForm3.FormCreate (Sender: TObject);
begin
//Корневой элемент дерева (диск)
root := tree.Items.Add (tree.Items.GetFirstNode, 'C:');
root.ImageIndex := 0;
root.SelectedIndex := 0;
SetExpanded(root, False);
end;
Здесь и далее в примере root позволяет быстро получать доступ к корневому элементу дерева. Используемая в листинге 4.27