Главная » Delphi » Программа для поиска файлов

0

Программу для поиска файлов (пока по названию, а в дальнейшем и по содержанию) мы сделаем, как и обещали, на основе программы Kodirovka из главы 8 (папка Glava8\3). Для этого ее придется сильно модернизировать, и мы не будем, как обычно, просто копировать проект, а переименуем и его, и единственный его модуль штатным способом — через пункты File | Save Project as (для проекта) и File | Save as (для модуля). Назовем проект Trace (что по смыслу не совсем точно, но звучит красиво и позволяет отличить программу от многочисленных Search), а модуль— poisk (папка Glaval3\2). Отметим, что мы на первых порах будем действовать "по старинке" — читать все из дискового файла, хотя это и категорически неправильно (см. замечание по этому поводу в главе 8). Я это делаю сознательно: мы доделаем программу по максимуму, чтобы вы убедились, насколько это "тормозная" штука — а потом исправим положение, использовав для чтения один из механизмов быстрого чтения файлов (применявшийся в главе 7 "маппинг"). Задумка моя состоит в том, чтобы на этом примере подвигнуть вас на изучение и использование механизмов виртуальной пвмяти— опыт показывает, что изучавшие Turbo Pascal сопротивляются этому до последнего. Результат сравнения алгоритмов будет достаточно впечатляющ, увидите.

Прежде всего, заменим картинку в imagel на другую аналогичную с новым названием программы Trace (файл trace.bmp в папке на диске) и заголовок формы иа Поиск файлов. Первое капитальное изменение— заменим

RichEdit на компонент WebBrowser (расположен последним пунктом в палитре Internet).

После замены придется внести изменения в текст модуля poisk. Прежде всего, удалим все, относящееся в RichEdit-. (если вы что-то забудете, то не сомневайтесь, на ошибку вам укажут). Затем добавим переменные: ftempname:string, nail: integer И ftemphtm:TextFiic. В начало процедуры ПО нажатию кнопки Button2 (Искать) вставим следующие строки:

ftempname:=ExtractFi1ePath(Application.ExeName)+’сrace000.htm'; assignfile (ftemphtm, ftempriame) ;

rewrite(ftemphtm); {создаем заново временный файл) closefile(ftemphtm);

Для удаления файла в конце программы создадим обработчик onDestroy формы:

proaedure TForml.FormDestroy(Sender: TObject); begin

if FileExists(ftempname) then

erase(ftemphtm); (уничтожаем временный файлI

end;

Далее создаем такую процедуру, расположив ее текст выше обработчика по кнопке Искать:

procedure с геа t eHTMLtext(s 11,s t:string); begin

if FileExists (ftempr.ame) then

append(ftemphtm) else exit; <вдруг его удали.пи?)

stl:='<В>1 +stl+ ‘</BXI>4-st+’ </IxBR>';

write(ftemphtm,stl);

closefile(ftemphtm);

end;

To есть мы просто-напросто создаем текстовый файл с HTML-тегами, которые форматируют нам текст, как надо1. Осталось его загрузить в WebBrowser. Для этого вместо всех процедур красивого вывода в RichEdit нужно добавить только две строки: createHTMLtext(stl,st);

WebBrowserl.Navigate(Pchar(ftenipname));

Самодеятельную процедуру удаления возможных пробелов в начале строки stpath заменим на официальную функцию Trim, которая сразу удаляет их с обеих концов. Для того чтобы не расписывать и дальше все мелкие исправления, я приведу полный текст новой процедуры по нажатию кнопки Button2 (Искать):

procedure TForrnl .Button2Click (Sender: TObject); /ищем файлы и обрабатываем их) begin

ftempname:=Ех tract FilePa Lh(Applica t ion.ExeName) +’traceOOO.htm'; assignfile(ftemphtm,ftempname);

rewrite(ftemphtm); {создаем заново временный фанл) closefile (fteniphtm); FlagCancel:=0; {флаг отмены) Button3.Enabled:=True; Iактивируем Отмену) Button2.Enabled:-False; {дезактивируем Поискi stpath:=Edit].Text; {названые папки)

if stpath=” then ex.it; {если оно пустое – сразу на выход)

stpath:=Trim(stpath); {удаляем возможные пробелы по концам)

if stpath=” then exit; {если пусто сразу на выход)

if stpath[length(stpath)]=’V then

delete(stpath,length(stpath),1);

{удаляем концевой знак ‘\’, если он есть)

try

ChDir(stpath); {проверяем, есть ли такая папка) except

Application.MessageBox(‘Несуществующая директория’,

‘ Error \MB_OK); exit; {если нету – на выход} end;

Forml.Labell.Caption:1; {очистили заголовок Label) Application.ProcessMessages; {чтобы сразу сработало) stsearch:=’\*';

stsearch:=stpath+stsearch; {строка для поиска файлов/ nfile:=0;

if FindFirst(stsearch,$23,sf)^0 then

{ $23 = не просматриваем системные файлы, тома и каталоп1) repeat

if FlagCanceloO then break;

{если флаг отмены we 0, то прерываем)

fname:=stpath+’\’+sf.Name; (полное имя файла}

if ANSIUpperCase (fname)=ANSIUpperCase(!:tempnanie) than continue;

(временный пропускаем) nall:=nall+l; (новый о&ъект) (все к одному регистру:\

if pos(ExtractFileExt(ANSIUpperCase(fname)),

ANSIUpperCase(stExt))<>0 then continue; (если расширение совпадает с запрещенным, то на выход) if not ReaaFileFormat then continue;(читаем файл) nfile:=nfile+l; (что-то получили}

Forml.Labell.Caption:=’Просмотрено: 1+IntToStr(nail)

+’ Найдено: ‘+IntToStr(nfile); st:=’ кодировка:’+st;

stl:=IntToStr(nfile)+’. ‘+fname; (номер найденного файла) createHTMLtext(stl,st); WebBrowserl.Navigate(Pchar(ftempname)); Application.ProcessMessages; (чтобы все прокрутилось I until FindNext(sf)<>0; (пока файлы не закончатся) Forml.Labe11.Capt i on: =’Просмотрено: ‘UntToStr(nail) +

‘ Найдено: ‘+IntToStr(nfile); stl:=Forml.Labell.Caption; st:='<BR><A 1®МБ="1">Поиск закомчен</А>1; createHTMLtext(stl,st); st:=ftempname+’tl'; WebBrowserl.Navigate(Pchar(st));

Form! .Editl. SetFocua; Iвозвращаем фокус a Eciill) Edit!.SelStart:=length(Editl.Text); Editl.SelText:=”; (курсор в конец текста Editl) FindClose(sf); (конец поиска) Button2.Enabled:=True; (активируем ПоискI Button3.Enabled:=False; (дезактивируем Отмену! end; (Button2)

Здесь переменная nail нам считает все просмотренные объекты. Обратите внимание, ЧТО В процедуре ПОИСКа МЫ вставили Строку if fname=ftempna:ne then continue, для того чтобы в процессе поиска избежать чтения, нашего временного файла. Прокрутку колесиком WebBrowser, по счастью, надежно поддерживает, и это есть великое благо, потому что добраться до его "скролл-баров" невозможно— в перечне свойств объекта они попросту отсутствуют, и позже вы поймете, почему. Но нам хочется остановить вывод по окончании поиска в конце текста, а не в начале — для наглядности. Для этого мы не стали ковыряться в недрах объекта WebBrowser, а попросту пометили последнюю строку обычной HTML-меткой (см. главу 16) и заставили браузер перейти на нее в конце вывода. Если несколько усложнить процедуру, вставив метку с самого начала, а затем перенося ее каждый раз в последнюю строку, можно даже добиться того, что WebBrowser будет прокручивать текст по мере вывода, но это будет достаточно сильно тормозить вывод.

Заметки на полях

Как аы поняли, компонент WebBrowser есть не что иное, как движок Internet Explorer — тот самый, интегрированный в Windows, из-за которого Microsoft чуть не засудили. Представляет собой он трансляцию соответствующих ActiveX- компонентов (см. главу 18). Причем он в Delphi довольно удобно реализован и позволяет при необходимости осуществлять далеко не только отображение HTML-файлов, для которого мы тут его используем. Главные его недостатки — медленная скорость работы и некоторые "глюки". Вообще в этой книге вопросы программирования для Интернета я стараюсь тщательно обходить. Причина, думаю, понятна — это особая тема, которая требует иного подхода и заслуживает отдельной книги. Но отображение удобного и компактного формата HTML требуется очень часто, и для WebBrowser мы сделали исключение. Можно ли найти для WebBrowser замену попроще и, главное, пошустрее? В принципе просмотрщиков HTML имеется сколько угодно, однако подавляющее большинство из них использует тот же самый Internet Explorer и польза от них сомнительная. Мне пришлось пользоваться одним из редких независимых от Internet Explorer компонентов под названием THtmiviewer (http://www.torry.net/vcl /internet/html/htm345d.zip). Впечатления вполне благожелательные, но он. во- первых, достаточно громоздкий (дистрибутив почти 3 Мбайта), во-вторых, платный, в-третьих, использует коммерческую библиотеку просмотра графики ilda32.dll, которую лучше таскать с собой (ее может не оказаться в системе, особенно в рвнних версиях Windows). По всем этим причинам пришлось от него отказаться.

Попробуем теперь ввести стандартный в таких случаях ползунок, показывающий ход процесса "перелопачивания" материала, и заодно обсудим вопрос— как рассчитать время до конца операции? О том, насколько это непросто— корректно отображать процесс, длительность которого априорно неизвестна, — могут свидетельствовать многочисленные "ляпы", допускаемые в таких случаях даже самыми продвинутыми программами. Нередко дело доходит до анекдота— один из подобных случаев представлен на рис. 13.2.

Рис. 13.2. Пример ошибки расчета времени при копироввнии файла

Анекдотических ситуаций мы постараемся избежать, но предупреждаю, что удовлетворительного результата все равно добиться очень сложно.

11родлим форму немного вниз и слева поставим на нее стандартный для таких случаев Progresses г, а чуть правее— Label 2, в который будем выводить время до конца операции (если у вас нет проекта перед глазами, то загляните на рис. 13.3, чтобы понять, что к чему). Для того чтобы правильно настроить ползунок, надо знать заранее объем предстоящей работы, и ничего не остается, кроме как перед началом поиска посчитать все файлы в заданной панке. Свойство step ползунка установим в I, и включим в проект такую процедуру:

procedure Calculatefiles; begin

Forml.ProgressBarl.Max:=0; if FindFirst (stsearch, 523, sf) =0 then

I $23 – не посматриваем системные файлы, тома п каталоги} repeat

Forml.ProgressBarl.Kax:=Forwl.ProgressBarl.Max+1; until FindNext". (sf) <>0; (пока фаш:ы не закончатся} FindClose(sf); {конец поиска I end;

lie очень умно устраивать такую долгую процедуру только для того, чтобы сделать красивый вывод, но выхода нет— кстати, в дальнейшем мы ее изменим, и она уже будет служить не только для красоты.

Раз уж мы имеем заранее полное число файлов, то нелишне его и продемонстрировать. Изменим в процедуре поиска вывод в Labeil на следующий оператор:

Form!.Label1.Caption:=’ Всего файлов 1 Tlnt’I’oStr (Forml. ProgressBarl .Max) +

‘ Просмотрено: ‘ UntToStr (nail) +’ Найдено: ‘+IntTo3tr(nfile);

Теперь вставим вызов calculatefiles перед самым началом цикла поискл. а процедуру инкремента progressBar — в середину:

Calculatefiles; {подсчитали фа?ты1 ProgressBarl. Posi’cion:=0; {установили в 0) if FindFirst(stsearch,$23,sf)=0 then

nail:=na11+1; {новый объект}

Forml.ProgressBarl.Steplt; {инкремент ProgressBar}

Обратите внимание на строку:

ProgressBarl.Position:=0;

Наиболее часто случающиеся "проколы" с "синей колбасой"2 появляются от того, что этой установкой пренебрегают— ползунок запоминает предыдущее состояние, в результате чего доходит до "упора" слишком рано и повторно начинает с начала, и потом наоборот, не доходит до конца. Ведь в конце предыдущего обращения к нему он находится вовсе не в нулевом состоянии — близком, но необязательно равном максимальному, и ошибка эта накапливается в дальнейшем. Впрочем, заставить ползунок работать строго, как положено — задача "не для белых людей". В этой программе, если вы обратили внимание, несмотря на все усилия, он также доходит до конца вовсе не всегда. Второй крупный недостаток этого компонента — то, что наличие ProgressBar довольно сильно тормозит процесс, позже вы в этом сами убедитесь.

Теперь попробуем справиться со временем. Введем две переменные типа TDateTime: tt и ttoid. Перед вызовом Caiculatefiles добавим такой оператор: ttold:=Time;. Далее мы будем каждый раз определять, сколько секунд прошло с этого момента, сопоставлять это время с количеством обработанных файлов и общим их количеством, и рассчитывать ориентировочное время окончания. Эта процедура будет выглядеть так:

procedure CalculateTime;

var ts:integer;

begin

tt:=Time; (текущее время) tsek:=SecondsBetween(tt,ttold); with Forml.ProgressBarl do begin

ts:=round((tsek’Max)/(Position));(расчет макс, времени) tsek:=ts-round((tsek*Position/Max)); (сколько сек осталось) end;

// tt:=tt-IncSecond(tt,tsek); end;

Разумеется, необходимо объявить переменную tsek и добавить в uses модуль DateUtils. Переменная tsek у нас будет иметь тип integer, хотя на самом деле ей положен тип int64, имеющий умопомрачительный диапазон почти до 10w степени по абсолютной величине. Трудно представить себе такое количество секунд, но диапазону типа integer в секундах соответствует всего 68 лет и в общем случае это, разумеется, оправданно. Имейте в виду, что все функции типа xxxBetween из модуля Date Is возвращают корректное положительное значение независимо от поряди параметров при вызове процедуры. О закомментированной строке — чут; лозже. Вставляем вызов этой процедуры и вывод результатов в середину поиска:

nfile:=nfile+I; {что-то получили) CalculateTime; (расчет времени} if tsek>0 than

Forml.Label2.Caption: = ‘OcT^ocb ‘+lntToStr(tsek) + ‘ сек';

В начале процедуры, одновременно с Labeli, заголовок Labei2 надо очищать:

Forml.Label1.Caption:=”; (очистили заголовок Labelij Forml,Label2.Caption:=’1; (очистили заголовок Label2}

Если мы запустим теперь программу, и зададим поиск, то получим нечто, подобное рис. 13.3, где поиск ведется в той же папке, что в главе 8, только похудевшей с тех пор на один файл.

Рис. 13.3. Работа программы Trace

Если вы исследуете работу программы для разных по объему папок, то увидите, что результаты расчета времени начинают приближаться к похожим на правду значениям только к концу процесса. Это. безусловно, следствие того, что файлы все разного размера и наш расчет очень приблизителен. Я привел здесь эту процедуру только для примера, и в дальнейшем мы ее не будем использовать ввиду ее сомнительной информативности — слишком легко тут попасть в ситуацию, отраженную на рис. 13.2 Автору этих строк еще ни разу не приходилось встречать программы, которые абсолютно верно отображали бы время до конца процесса априорно неизвестной длительности. Единственный, пожалуй, относительно корректный пример дают сетевые загрузчики файлов, но весь фокус в том, что как раз там длительность априорно известна — т. к. более-менее точно известна скорость перекачки. Если же она меняется непредсказуемо, то и эти программы попадают впросак. Обратите, например, внимание на то, что любой такой загрузчик при демонстрации скорости перекачки (обычно в кбайт/сек) сначала всегда показывает невероятно высокую скорость— загрузка начинается на всякий случай заранее, пока пользователь еще мучительно размышляет на вечную тему "а надо ли?", и отсюда подобные "ляпы". Показательно, что никто даже и не пытается исправить эту ситуацию, видимо, зная по опыту, что будет только хуже.

Альтернативную возможность представления результатов расчета времени дает закомментированная строка: если вы ее раскомментируете и замените в операторе вывода в Labei2 выражение intTostr (tsek) на TimeTostr(tt), то получите вывод в виде ЧЧ:ММ:СС. Следует отметить, что работа с форматами времени и даты требует опыта, терпения и представляет собой довольно сложный процесс: так, вывод через TimeTostr у меня дает результаты без ведущего нуля, но почему-то только в значении часов — кому вообще мог прийти в голову формат времени без ведущего нуля в разрядах? Дня получения заведомо определенного представления даты/времени во всех таких случаях следует использовать функцию FormatDateTime (или DateTimeToString) — не нужно пытаться, например, просто дополнить строку нулем. Это следует делать по той причине, что работа функции TimeTostr зависит от системных установок и вы можете попасть впросак, если запустите вашу программу, к примеру, под английской версией. Другой нюанс, связанный со временем — здесь мы выдаем только разницу ао времени в часах-минутах- секундах, а что будет, если поиск придется на полночь? В общем, есть над чем поломать голову.

На данном этапе это еще не законченная программа, конечно, потому она пока ничего не ищет, просто перебирает все текстовые файлы, по ходу дела определяя кодировку. Разумеется, такая операция не имеет большого практического смысла, и в следующей главе мы займемся усовершенствованием программы и превращением ее в программу поиска по тексту.

Источник: Ревнч Ю. В.  Нестандартные приемы программирования на Delphi. — СПб.: БХВ-Петербург, 2005. — 560 е.: ил.

По теме:

  • Комментарии