Програмний пошук файлів

Програмний пошук файлів

У цьому уроці ми з вами ознайомимося з основними принципами програмної організації пошуку файлів. Для початку визначимося, навіщо нам це може бути потрібно. Наприклад, вам потрібно при запуску програми на виконання просканувати певний каталог на присутність DOC файлів, і при наявності таких відкрити їх на редагування або надрукувати. А як вам така ідея: фоновий пошук EXE файлу в мережі, і при виявленні нової версії, автоматичне оновлення.


Багатьом відомі програми, де можна шукати файли, правила пошуку файлу. Файли можна шукати як з файлових командирів (нортон, вовків, дос навігатор, фар), так в будь-якій операційній системі. В операційній системі windows діалогове вікно пошуку файлу викликається "Пуск" - "Пошук" - "Файли і папки". У вікні, необхідно задати умову шуканого файлу (назва, маска) і шлях початкового пошуку (каталог). На інших вкладках цього діалогового вікна можна розширити можливості пошуку за датою зміни, по вмісту тексту, за розміром.

Згадаймо правила пошуку файлів. Ви можете задати як ім'я шуканого файлу, так і його маску, якщо назва невідомо чи необхідно знайти кілька. Тобто застосовуючи спеціальний шаблон пошуку, ви можете організувати умови вибірки знайдених файлів. Відразу обмовлюся, що пошук можна застосовувати як до файлів, так і до каталогів. Будемо їх називати елементами файлової системи. У шаблон маски шуканих елементів може входити:
1. Літери і цифри в назві і розширенні.
2. Символ * (зірочка, математичний знак "помножити"), який замінює будь-яку кількість всіляких букв і цифр у назві або розширенні.
3. Символ. (Знак питання), який замінює одну букву або цифру в назві або розширенні шуканого елемента.
Наприклад, ви шукайте все текстові файли з розширенням TXT. В поле імені шуканого файлу вам потрібно ввести "* .TXT" (пишеться без лапок) і система знайде всі такі файли в зазначеному диску або каталозі. Якщо вам треба знайти всі файли з назвою semen, то в поле пошуку файли наступним чином "semen. *". Якщо вам потрібно знайти елементи з третьою буквою k і з першою літерою t в розширенні, то вводите "?? k * .t *". Тут знак питання вказує на будь-який символ, третім символом по порядку йде буква k, далі назва файлу (каталогу) може складатися з будь-якої кількості букв і цифр, вказуємо зірочку. У розширенні перша буква t, далі слід будь-яке розширення.
Примітка: файли і каталоги в операційній системі windows шукаються без урахування регістру, тобто малих і великих літер не розрізняються.

Тепер розглянемо програмний пошук файлів за допомогою мови програмування object pascal.

Вся організація циклу пошуку, а саме це і є цикл з продовженням пошуку, зводиться до:
1. Завдання умов пошуку. Це каталог і маска шуканого елемента або елементів, атрибути елемента (ів). При завданні умов пошуку відразу відбувається пошук першого підходящого під умову. Це функція FindFirst.
2. Продовження пошуку наступного елемента по заданих в першому пункті умов. Це функція FindNext і вона може бути викликана скільки завгодно раз, поки всі файли і каталоги, що задовольняють умові, що не будуть знайдені.
3. Закриття пошуку і звільнення пам'яті, що виділяється системою під пошук. Команда FindClose.

FindFirst
(КАТАЛОГ_ПОІСКА_І_МАСКА_ФАЙЛА,
АТРІБУТИ_ІСКОМОГО_ФАЙЛА. ПОІСКОВОЯ_ПЕРЕМЕННАЯ);

де: Каталог для пошуку і маска шуканого елемента - строкова величина, що має тип String, може, наприклад, містити 'c: \ *. *' - все елементи в корені диска С. Зверніть увагу, що вказується повний шлях для пошуку.

Атрибути шуканого елемента це призначені для користувача або системні атрибути, які може мати файл (каталог, мітка диска). Ось їх перелік:
faReadOnly - Файли "тільки читання". Такий атрибут встановлюється на файли, які не рекомендовано змінювати, видаляти. Такий атрибут мають файли, наприклад, записані на компакт-дисках.
faHidden - Приховані файли. При звичайних установках браузера та командира ці файли невидимі.
faSysFile - Системні файли.
faVolumeID - Файл мітки диска. Такий елемент в своєму імені має назву диска (максимум 11 символів).
faDirectory - Атрибут ознаки каталогу.
faArchive - Звичайний файл. За замовчуванням встановлюється на заново створюваних файлах.
faAnyFile - Якщо встановити в якості атрибута шуканих елементів, то буде проведений пошук по усім вищесказаним атрибутам.
Ці вам потрібно шукати тільки елементи, що мають атрибут "каталог" і "прихований", то можна застосувати знак математичного складання, наприклад faDirectory + faHidden.

Пошукова змінна має тип TSearchRec. У неї, при успішному результаті пошуку, буде занесено всі необхідні дані про знайдений файловому елементі.

Оскільки FindFirst є функцією, то вона повинна сама повертати деяке значення. Це значення має тип Integer і означає результат пошуку файлу (код помилки пошуку). Якщо файл знайдений, то приймає нульове значення.

Ця операція продовжують пошук, заданий в функції FindNext. Повертає значення результату пошуку (нульове в разі успішного пошуку).

Закриває пошук і звільняє пам'ять, виділену системою під пошук.

Тепер розглянемо приклад. Припустимо, нам треба знайти всі файли і каталоги в каталозі DELPHI, що знаходиться на диску C. Надалі, ви можете самостійно, змінюючи маску, міняти умови пошуку. Для форми з компонентом ListBox1 і кнопкою Button1 реакція на OnClick по кнопці:

procedure TForm1.Button1Click (Sender: TObject);
Var SR: TSearchRec; // пошукова змінна
FindRes: Integer; // змінна для записи результату пошуку
begin
ListBox1.Clear; // очищення компонента ListBox1 перед занесенням в нього списку файлів

FindRes: = FindFirst ( 'c: \ delphi \ *. *', FaAnyFile, SR); // завдання умов пошуку і початок пошуку

While FindRes = 0 do // поки ми знаходимо файли (каталоги), то виконувати цикл
begin
ListBox1.Items.Add (SR.Name); // додавання в список назву знайденого елемента
FindRes: = FindNext (SR); // продовження пошуку за заданими умовами
end;
FindClose (SR); // закриваємо пошук
end;

Представлений приклад коду, в принципі, є основою для організації більш поглибленого пошуку, пошуку файлів за часом створення, за що містяться словами. Якщо ви запустите цю програму на виконання, то при натисканні на кнопку Button1 ви побачите в списку в першій і другій рядку елементи "." і "..". Це елементи, які мають атрибут "каталог". Перший містить зв'язок з кореневим каталогом диска, другий містить зв'язок до каталогом верхнього рівня. З другим ви зустрічаєтеся в дискових командних оболонках, наприклад нортон, коли вибираєте каталог ".." і натискаєте на "введення". Тим самим ви потрапляєте в каталог на рівень вище. Природно, в нашій пошуковій програмі такі елементи не треба вносити в список, тому ми ігноруємо їх знаходження. Виправляємо процедуру натискання на кнопку Button1:

procedure TForm1.Button1Click (Sender: TObject);
Var SR: TSearchRec;
FindRes: Integer;
begin
ListBox1.Clear;

FindRes: = FindFirst ( 'c: \ delphi \ *. *', FaAnyFile, SR);
While FindRes = 0 do
begin
if ((SR.Attr and faDirectory) = faDirectory) an d // якщо знайдений елемент каталог і
((SR.Name = '.') Or (SR.Name = '..')) then // він має назву "." або "..", тоді:
begin
FindRes: = FindNext (SR); // продовжити пошук
Continue; // продовжити цикл
end;

ListBox1.Items.Add (SR.Name);
FindRes: = FindNext (SR);
end;
FindClose (SR);
end;

В цьому випадку, при знаходженні каталогу з ім'ям "." або з ім'ям ".." програма продовжить обробку циклу пошуку без виведення знайденого імені елемента в компонент списку ListBox1.

Тепер розглянемо тип TSearchRec. Він має в собі кілька корисних властивостей:
Name - назва знайденого каталогу (файлу);
Size - розмір файлу в байтах;
Attr - атрибути каталогу (файлу);
Time - упаковане значення часу і дати створення каталогу (файлу).

Всі перераховані вище властивості ми вже розглянули або вони зрозумілі відразу, за винятком властивості Time. Воно має тип Integer і містить в собі упаковане значення дати і часу створення файлу. Розпакування проводиться за допомогою функції FileDateToDateTime, яка в результаті повертає значення дати і часу.
Тепер додамо в нашу форму компонент DateTimePicher1 (сторінка Win32) і допишемо кілька рядків.

procedure TForm1.Button1Click (Sender: TObject);
Var SR: TSearchRec;
FindRes: Integer;
begin
ListBox1.Clear;

FindRes: = FindFirst ( 'c: \ delphi \ *. *', FaAnyFile, SR);
While FindRes = 0 do
begin
if ((SR.Attr and faDirectory) = faDirectory) and
((SR.Name = '.') Or (SR.Name = '..')) then
begin
FindRes: = FindNext (SR);
Continue;
end;
if FileDateToDateTime (SR.Time) begin
FindRes: = FindNext (SR); // продовжити пошук
Continue; // продовжити цикл
end;

ListBox1.Items.Add (SR.Name);
FindRes: = FindNext (SR);
end;
FindClose (SR);
end;

Як ви вже помітили, ми відбираємо файли і каталоги за датою створення, починаючи з зазначеної в компоненті DateTimePicker1.

Тепер спробуємо організувати пошук файлів у всіх вкладених каталогах. Це не так просто, як може здатися на перший погляд. Нам доведеться вручну організовувати весь цикл входу-виходу з каталогу, перебір файлів. Трохи важкувато матеріал, але можливо ті з вас, хто вже працював з мовою програмування pascal або іншим, знайомі з технологією многократности і многовложенності використання одного і того ж програмного коду. Коротко поясню алгоритм роботи такої програми.
1. Завдання початкових умов пошуку, пошук першого елемента.
2. Якщо знайдений файл, то виводимо його і відповідно обробляємо (виводимо в список, відкриваємо, видаляємо і т.п.).
3. Якщо знайдений каталог, то починаємо нову процедуру пошуку. Але програмний код залишається тим самим. Ми просто заново викликаємо і входимо в цю ж процедуру пошуку.
4. Обробляємо таким же чином всі вкладені в цей каталог файли і каталоги (починаємо новий пошук в знайденому каталозі).
5. Якщо елементів у вкладеному каталозі більше немає, то обробка процедури пошуку в ньому завершується, і ми виходимо з неї. При цьому ми опиняємося в тому ж місці, звідки і викликали цю процедуру. Але вона була викликана з цієї ж процедури. Тому програма продовжує своє виконання далі з моменту повернення.
Таким чином, скільки витків програма намотує на так званий клубок, стільки витків вона і розмотати. Програма на виконанні проходить все дерево вкладених каталогів, виконуючи один і той же шматок програмного коду! І при цьому дані умовам пошуку не переплутуються, і для кожної унікальної процедури вони зберігаються.

Розглянемо приклад. Створіть новий проект. Для створення окремої процедури пошуку нам потрібно оголосити її у відповідному розділі (створюємо її вручну, тому й самостійно оголошуємо).

У розділі public пишемо рядок:

А в розділі коду програми, до слова "end." вставляємо порожній каркас процедури

procedure TForm1.FindFile (Dir: String);
begin

На форму вставляємо компонент списку ListBox1, Button1, Edit1. Для компонента Edit1 властивість Text встановлюємо в "c: \ delphi". Зверніть увагу на останній символ, знак "", присутність якого в початковому шляху пошуку обов'язково. Далі процедура OnClick для кнопки Button1 виглядає наступним чином:

procedure TForm1.Button1Click (Sender: TObject);
begin
ListBox1.Clear; // очищення списку файлів
FindFile (Edit1.Text); // пошук файлів з початковими умовами, заданих в Edit1
end;

Створена нами вручну процедура пошуку:

procedure TForm1.FindFile (Dir: String);
Var SR: TSearchRec;
FindRes: Integer;
begin
FindRes: = FindFirst (Dir + '*. *', FaAnyFile, SR);
While FindRes = 0 do
begin
if ((SR.Attr and faDirectory) = faDirectory) and
((SR.Name = '.') Or (SR.Name = '..')) then
begin
FindRes: = FindNext (SR);
Continue;
end;

if ((SR.Attr and faDirectory) = faDirectory) then // якщо знайдений каталог, то
begin
FindFile (Dir + SR.Name + '\'); // входимо в процедуру пошуку з параметрами поточного каталогу + каталог, що ми знайшли
FindRes: = FindNext (SR); // після огляду вкладеного каталогу ми продовжуємо пошук в цьому каталозі
Continue; // продовжити цикл
end;

ListBox1.Items.Add (SR.Name);
FindRes: = FindNext (SR);
end;
FindClose (SR);
end;

Якщо ви в компоненті Edit1 в якості початкового умови пошуку файлів задасте кореневу папку диска, наприклад "З:", то ви отримаєте повний перелік всіх файлів на даному диску. Зверніть увагу на швидкість пошуку файлів і швидкість роботи вашої програми.