Як приклад використання рекурсії розглянемо задачу пошуку файлів. Нехай потрібно отримати список всіх файлів, наприклад, з розширенням 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.