Програма пошуку в потоці CBuilder

У нашому другому прикладі роботи з потоками ми збираємося написати програму пошуку, що використовує потоки Вона дозволить шукати задану рядок у заданому каталозі Також ми надамо можливість вибрати маску файлів (наприклад, всі вихідні файли * cpp), за якими буде відбуватися пошук Коли буде натиснута кнопка Почати пошук, форма запустить потік, який стане шукати файли в заданому каталозі, які містять потрібний рядок, і виводити імена файлів у вікно списку, що знаходиться в головній формі

Поки потік працює, користувач може захотіти подивитися будь-який з цих файлів, така можливість і надається в нашій програмі Ми дозволимо користувачеві вибирати файл зі списку подвійним клацанням миші, після чого завантажиться форма з цим файлом в текстовому вигляді (якщо, звичайно, файл влізе в память) Ми не можемо відразу дізнатися, що потік закінчив роботу, так що не можемо обробляти елементи в міру їх надходження Однак чим добре використання ня потоку: програма тільки й робить, що обробляє введення користувача і залишає управління елементами у вікні списку на піклування потоку Ні необхідності перевіряти

повідомлення від користувача під час пошуку, так як потік працює повністю самостійно Ми пишемо звичайний код, наче потоку і немає

У цьому проекті дві форми У головній формі знаходяться поля введення для завдання параметрів пошуку і вікно списку, в якому міститимуться результати На другій формі знаходиться тільки компонент TMemo (Записна книжка), який ми використовуємо для відображення вмісту файлу На рис 132 показана головна форма програми, а на рис 133 – вторинна форма

Рис 132 Головна форма програми «пошук в потоці»

Рис 133 Форма додатка «пошук в потоці», що відображає файли

Головна форма програми будується з трьох міток, трьох полів введення, кнопки і вікна списку Поля введення тільки передають нам інформацію Ніякої перевірки введених даних ми здійснювати не будемо Якщо користувач введе неправильні дані в поля Маска файлів або Каталог, то ми просто нічого не знайдемо Якби ми писали комерційне додаток, то, природно, ми б додали кнопку огляду диска, що дозволило б користувачеві вибрати каталог, а також, ймовірно, надали б можливість вибрати одну із зумовлених масок файлів Проте це не має прямого відношення до нашого прикладу, так що не станемо цим займатися

По-перше, треба розібратися з обробкою кнопки Почати пошук, яка буде починати процес пошуку, запускаючи потрібний потік Створіть обробник натискання на кнопку Почати пошук і

помістіть в обробник наступний код:

void __Fastcall TForm1::Button1Click(TObject *Sender)

{

pThread = new TSearchThread(Edit2-&gtText, Edit3-&gtText, Edit1-&gtText, FALSE)

SetOkToSearch( false )

}

Все, що ми робимо, – це створюємо новий потоковий обєкт і передаємо йому дані, введені користувачем в три поля введення на формі Ми пізніше розберемося з створенням класу потоку, поки ж будемо вважати, що там все працює Метод SetOkToSearch використовується для вказівки, чи відбувається в даний момент пошук, а також для заборони і дозволу кнопки пошуку Ось реалізація цієї функції, яку ви можете написати прямо в заголовному файлі, в визначенні класу:

void SetOkToSearch(bool bSearchOK)

{

Button1-&gtEnabled = bSearchOk

}

Навіщо нам для цього знадобилася функція Справа в тому, що таким чином ми захищаємо реалізацію коду, що забороняє кнопку Якби нам знадобилося робити що-небудь ще в цій функції, наприклад оновлення інформації в панелі стану, висновок якого-небудь повідомлення або навіть запис події в якій-небудь зовнішній файл, то класи, що використовують цю функцію, нічого не знатимуть про всі ці побічні дії Все, що знають викликають функції про цей метод, – це що він зупиняє пошук або ж починає новий

Розібравшись з цим, наступним справою треба розібратися з відображенням текстового файлу, який користувач вибирає подвійним клацанням миші у вікні списку Створіть обробник події DblClick (Подвійне клацання) для обєкта ListBox1 (Вікно списку) і додайте в обробник наступний код:

void __fastcall TForm1::ListBox1DblClick(TObject *Sender)

{

/ / Отримати імя обраного файлу

int nIdx = ListBox1-&gtItemIndex if ( nIdx = -1 )

{

AnsiString strFile = ListBox1-&gtItems-&gtStrings[nIdx]

/ / Створити нову форму для перегляду файлу

pFileViewer = new TForm3(this)

pFileViewer-&gtMemo1-&gtLines-&gtLoadFromFile( strFile ) pFileViewer-&gtShow()

}

}

У цьому методі ми отримуємо імя файлу, яке користувач вибрав з вікна списку Список міститиме імена файлів з вказівкою повного шляху, так що функція може вважати, що імя файлу коректно (ми переконаємося в цьому пізніше) і просто використовувати його для відображення файлу Само відображення цілком на совісті обєкта VCL TMemo, Який сам знає, як безпосередньо завантажувати свій вміст з файлу Використовуючи метод LoadFromFile, Ми завантажуємо весь вибраний файл прямо в память компонентаTMemo, Що знаходиться на вторинній формі

Зауважте, що вам взагалі не треба писати ніякого коду для цієї вторинної форми (клас TForm3) Вся робота за вас вже зроблена Приємно іноді нічого не писати, чи не так

Все це реалізує всі візуальні аспекти завантаження форми, пошуку і відображення тексту файлів, які підходять під задані критерії Так що єдине, що залишилося – це сам пошук Пошук повинен відбуватися всередині нашого потокового обєкта, так що настала пора зайнятися і цим

У даному прикладі потік створюється точно так само, як і в попередньому Використовуй ті систему створення обєкта «потік», вбудовану в CBuilder, для створення нового потокового класу з назвою TSearchThread CBuilder згенерує вихідний файл (в даному прикладі Unit2cpp), в якому буде визначений клас

Перша зміна, яку треба зробити, зачіпає конструктор класу Окрім підготовки потоку до запуску, нам потрібно встановити початкові значення для всіх змінних-членів класу Згадавши про попередження, яке я давав трохи вище, про небезпеку запуску потоку до того, як закінчиться ініціалізація, ми також додамо і цей невеликий шматочок коду, що захищає наш потік Ось повний код конструктора класу:

__fastcall TSearchThread::TSearchThread(AnsiString strDir,

AnsiString strMask, AnsiString strText, bool CreateSuspended)

: TThread (true)

{

FstrDirectory = strDir

if ( FstrDirectory[FstrDirectoryLength()-1] = \\ ) FstrDirectory += \\;

FstrFileMask = strMask FstrSearchText = strText if ( CreateSuspended ) Resume()

}

Укупі з цими змінами нам потрібно трошки підправити та заголовки для класу потоку, щоб він містив опису змінних і методів класуTSearchThread Ось зміни (виділені підсвічуванням) в заголовному файлі Unit2h для класу TSeachThread:

class TSearchThread : public TThread

{

private: protected:

void __fastcall Execute()

private:

AnsiString FstrDirectory AnsiString FstrFileMask

AnsiString FstrSearchText

AnsiString FstrCurFileName

int DoFind( AnsiString strSearchText, AnsiString&amp text )

public:

__fastcall TSearchThread(AnsiString strDir, AnsiString strMask, AnsiString strText, bool CreateSuspended) void __fastcall AddToListBox(void)

}

Змінні-члени класу FstrDirectory, FstrFileMask і FstrSearchText використовуються для визначення файлів, за якими проводиться пошук, а також тексту, який потрібно в них знайти Мінлива, яка вказує імя поточного файлу (FstrCurFileName), потрібна тут, так як метод, через який відбувається спілкування з обєктом VCL на формі – вікном списку, – не має параметрів І нарешті, в секцію private ми додаємо функціюDoFind, Яка визначатиме, чи містить файл шуканий рядок

Коли ми проініціалізувати потік і запустили його (викликавши метод Resume в кінці конструктора), в гру вступає метод Execute У попередньому прикладі метод Execute виконувався вічно, так як не було розумних причин для його використан ня У нашому ж випадку мета зрозуміла: шукати серед заданого кінцевого числа файлів всі ті, які містять задану рядок тексту Тому нам не потрібен вічний цикл нам потрібно переривати виконання потоку, коли запас файлів для пошуку вичерпається Ось код методу Execute класу TSearchThread:

void __fastcall TSearchThread::Execute()

{

/ / Проходимо по всіх файлів, які підходять під маску

/ / Для цього використовуємо функцію Win32 API WIN32_FIND_DATA FindFileData

AnsiString strSearchFiles = FstrDirectory + FstrFileMask

HANDLE hFirstFileHandle = FindFirstFile( strSearchFilesc_str(), &ampFindFileData ) while ( hFirstFileHandle &amp&amp Terminated )

{

/ / Намагаємося відкрити файл на читання

FILE *fp = fopen(FindFileDatacFileName, &quotr&quot ) FstrCurFileName = FstrDirectory + FindFileDatacFileName if (fp == NULL)

{

if ( FindNextFile( hFirstFileHandle, &ampFindFileData)

== FALSE )

break continue

}

/ / Шукаємо в даному файлі построчно

char szBuffer[ 256 ] while ( feof(fp) )

{

if ( fgets(szBuffer, 255, fp) == NULL ) break

AnsiString s = szBuffer

if ( DoFind( FstrSearchText, s ) )

{

Synchronize(AddToListBox) break

}

}

/ / Закриваємо файл і переходимо до наступного в списку

fclose(fp)

if ( FindNextFile( hFirstFileHandle, &ampFindFileData )

== FALSE )

break

}

Form1-&gtSearchComplete()

}

Цей код слід простому і прямолінійним процесу По-перше, маска файлу і каталог використовуються для створення списку файлів для пошуку, використовуючи функції API FindFirstFile і FindNextFile, Які ми розглядали вище, в розділі, присвяченому Windows API

Кожен файл відкривається (якщо не відкривається, то ми його пропускаємо), і з нього по одній читаються рядки тексту Якщо потрібний рядок тексту знайдена в цьому рядку файлу, то файл закривається, і імя файлу додається в вікно списку форми, і внутрішній цикл пошуку по файлу завершується Коли файл вже оброблений, він закривається і береться наступний за допомогою функції API Поки все це відбувається, користувач може спокійно вибрати будь-який файл зі списку і подивитися його, використовуючи форму перегляду файлів, що ми реалізували вище в цьому розділі

Коли всі файли переглянуті або потік завершив свою роботу, викликається метод форми SearchComplete Цей метод, наведений в наступному лістингу, просто завершує потік, якщо той не був зупинений або ще не завершився, а потім знову робить доступною кнопку Почати пошук на формі Це готує форму до другого, третього пошуку і т д:

void __fastcall TForm1::SearchComplete(void)

{

pThread-&gtTerminate() SetOkToSearch( true )

}

Код функції пошуку вкрай простий Я додав дещо зайве, наприклад підрахунок кількості знайдених рядків тексту, якщо раптом вам захочеться ввести ваговий коефіцієнт, який вказує, наскільки даний файл підходить користувачеві, щоб відображати його разом з імям файлу у вікні списку Якщо ви заробите мільйон доларів на цьому непоказному шматочку коду, надішліть мені листівку з Бермуд, добре

/ / Метод здійснює тупий пошук рядка

/ / Тексту у вхідному рядку

int TSearchThread::DoFind(AnsiString strSearchText, AnsiString&amp text)

{

int i=0

int nCount = 0

while ( i &lt textLength() )

{

if ( strncmp(textc_str()+i, strSearchTextc_str(), strSearchTextLength()) )

{

i += strSearchTextLength() nCount ++

}

else

{ i++

}

}

return nCount

}

Нарешті, останньою частиною головоломки є функція AddToListBox Ця функція, яку ми налаштували на синхронізацію з формою в конструкторі класу, відповідає за додавання імені поточного файлу в вікно списку, що знаходить ся в головній формі Нам не потрібно хвилюватися про те, чи працює в цей час хтось зі списком, так як ця функція синхронізована з формою Ось код функції AddToListBox:

void __fastcall TSearchThread::AddToListBox(void)

{

if ( FstrCurFileNameLength() )

Form1-&gtListBox1-&gtItems-&gtAdd( FstrCurFileName )

}

Цей код просто перевіряє, чи є взагалі хоч які-небудь символи в імені файлу (а раптом), А потім додає імя файлу в кінець списку на формі Після чого користувач може вибрати бажаний файл з списку подвійним клацанням миші в будь-який момент роботи програми

Рис 134 Програма фонового пошуку в дії

На рис 134 показаний типовий пошук файлів на моєму жорсткому диску, якому було сказано шукати всі вихідні файли (* Cpp), що містять слово «Text» Результати показані у вікні списку, і в той же час один з файлів показаний у вікні перегляду файлів Пошук закінчений, і кнопка Почати пошук знову доступна для запуску нового пошуку

Джерело: Теллес М – Borland C + + Builder Бібліотека програміста – 1998

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*