Примеры программ
Поиск
файлов
В качестве
примера использования рекурсии рассмотрим задачу поиска файлов. Пусть нужно
получить список всех файлов, например, с расширением bmp, которые находятся в
указанном пользователем каталоге и во всех подкаталогах этого
каталога.
Словесно
алгоритм обработки каталога может быть представлен так:
1. Вывести
список всех файлов удовлетворяющих критерию запроса.
2. Если в
каталоге есть подкаталоги, то обработать каждый из этих
каталогов.
Приведенный
алгоритм (его блок-схема представлена на рис. 12.4) является рекурсивным: для
того чтобы обработать подкаталог, процедура обработки текущего каталога должна
вызвать сама себя.
Рис. 12.4.
Рекурсивный алгоритм поиска файлов
Вид диалогового
окна программы приведен на рис. 12.5, текст — в листинге 12.3.
Поле Файл
(Edit1) используется для ввода имени искомого файла или маски (для поиска
файлов одного типа). Имя каталога, в котором нужно выполнить поиск, можно ввести
непосредственно в поле Папка или выбрать из стандартного диалогового окна
Обзор папок, которое появляется в результате щелчка на кнопке Папка.
Окно Обзор папок (рис. 12.6) выводит на экран стандартная функция
Seiectoirectory. Следует обратить внимание, что имя каталога, который
используется в диалоговом окне Обзор папок в качестве корневого, должно
передаваться функции SeiectDirectory как Строка WhideChar. Для Преобразования
обычной строки в строку WideChar использована функция
StringToWhideChar.
Рис. 12.5.
Окно программы Поиск файлов
Рис. 12.6.
Диалоговое окно Обзор папок появляется в результате щелчка на кнопке
Папка
Основную работу
выполняет рекурсивная функция Find. У функции Find один-единственный параметр —
структура searchRec, которая используется функциями FindFirst и FindNext для
поиска соответственнопервого и следующего файла, удовлетворяющего критерию
поиска. Следует обратить внимание на то, как осуществляется перебор каталогов в
текущем каталоге. Если текущий каталог не корневой, то помимо обычных, то есть
имеющих имя, в каталоге есть еще два каталога: .. и ., которые обозначают
каталог предыдущего уровня. Эти два каталога не обрабатываются, так как при
входе в эти каталоги фактически выполняется выход (переход) в родительский
каталог. Если этого не учесть, то программа зациклится.
Листинг
12.3. Программа поиск файлов
// поиск файла в указанном каталоге и его подкаталогах
// используется рекурсивная процедура Find
unit
FindFile_;
interface
uses
Windows, Messages, SysUtils, Variants,
Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, FileCtr;
type
TForm1 =
class(TForm)
Editl: TEdit; // что
искать
Edit2: TEdit; // где
искать
Memo1: TMemo; // результат
поиска
Button1: TButton; //
кнопка Поиск
Button2: TButton; //
кнопка Папка
Label1:
TLabel;
Label2:
TLabel;
Label3:
TLabel;
Label4:
TLabel;
procedure
Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations
}
public
{ Public declarations }
end;
var
Form1:
TForm1;
implementation
{$R *.dfm}
var
FileName: string;
// имя или маска искомого файла
cDir:
string;
n: integer; // кол-во
файлов, удовлетворяющих запросу
// поиск файла в текущем
каталоге
procedure
Find;
var
SearchRec: TSearchRec; //
информация о файле или каталоге
begin
GetDir(0,cDir); // получить имя текущего каталога
if cDir [length
(cDir) ] <> 'V then cDir := cDir+'\';
if FindFirst(FileName, faArchive,SearchRec) = 0
then
repeat
if (SearchRec.Attr and faAnyFile) = SearchRec.Attr
then
begin
Form1.Memo1.Lines.Add(cDir + SearchRec.Name);
n := n + 1; end; until
FindNext(SearchRec) <> 0;
// обработка подкаталогов текущего каталога
if FindFirst('*',
faDirectory, SearchRec) = 0 then repeat
if (SearchRec.Attr
and faDirectory) = SearchRec.Attr then begin
// каталоги .. и . тоже каталоги,
// но в них входить не надо .'.'.'
if
SearchRec.Name[1] <> '.' then begin
ChDir(SearchRec.Name);// войти в каталог
Find; // выполнить поиск в подкаталоге
ChDir('..');// выйти из каталога
end;
end;
until FindNext(SearchRec) <> 0;
end;
/ возвращает каталог, выбранный пользователем
function GetPath(mes: string):string;
var
Root: string; // корневой каталог
pwRoot : PWideChar; Dir: string;
begin
Root := '';
GetMem(pwRoot, (Length(Root)+1) * 2);
pwRoot := StringToWideChar(Root, pwRoot, MAX_PATH*2);
if
SelectDirectory(mes, pwRoot, Dir) then
if length(Dir) =2 // пользователь выбрал корневой каталог
then GetPath :=
Dir+'\' else GetPath := Dir else
GetPath := '';
end;
щелчок на кнопке
Поиск
procedure TForml.ButtonlClick(Sender: TObject);
begin
Memo1.Clear; // очистить
поле Memol
Label4.Caption :=
'';
FileName := Edit1.Text; //
что искать.
cDir := Edit2.Text; // где
искать
n:=0; // кол-во найденных
файлов
ChDir(cDir); // войти в
каталог начала поиска
Find; // начать
поиск
if n = 0
then
ShowMessage('Файлов, удовлетворяющих критерию поиска нет.')
else Label4.Caption := 'Найдено файлов:' + IntToStr(n);
end;
// щелчок на кнопке
Папка
procedure
TForml.Button2Click (Sender: TObject);
var
Path: string;
begin
Path := GetPath('Выберите
папку');
if Path <>
''
then Edit2.Text := Path;
end;
end.