Главная » Delphi » Поиск заданной строки

0

Теперь приступим к поиску. Установим на paneii еще один Edit (Edit?.) выше первого (соответствующим образом сдвинув все компоненты). Перед ними обоими установим два компонента Label для заголовков (шрифт полужирный" Arial 10-го кегля), а ниже Edit2 два компонента RadioButton с заголовками Фразу целиком (рис. 14.1) И Любое из слов, которые назовем RadioButtonAND и RadioButtonOR (т. к. они установлены на основной панели, то связи с RadioButtoni и RadioButton2 не будет). На самом деле это не совсем точные названия, продвинутый поиск требует трех функций: "Фразу целиком", "Все слова" (собственно функция AND) и "Любое из слов" (функция OR), а по большому счету и этим не ограничивается, см. далее.) Но удовлетворимся тем, что функция "Любое из слов" перекроет также и варианты, когда встречаются все слова, но в разных местах (как видите, я все время остаь- ляю большой простор будущим поколениям для самоусовершенствования!).

В Label3 (напротив Edit2) НЭПИШеМ Искать что, а В Label4 (напротив Editl) — Искать где. RadioBur.tonAMD должна быть отмечена по умолчанию. У обоих компонентов Edit установим Autosize и AutoSelect в False. Окончательно верхняя часть окна программы должна выглядеть так, как показано на рис. 14.1.

Рис. 14.1. Заголовок программы Trace для поиска заданной строки

В обработчике события onshow формы (а также в конце процедуры поиска) заменим три строки установок для редактора Editl на следующие:

procedure TFonnl.FormShowtSender: TObject); (при запуске/ begin

Edit1.SelStart:=length(Editl.Text); Editi.SelText:=”; (курсор в конец текста Editl} Edit2.SetFocus; /фокус в Edit2} Edit2.SelStart:=length(Edit2.Text); Edit2.SelText:=”; (курсор в конец текста Edit2) end;

Для того чтобы обеспечить удобный запуск поиска, надо обязательно связать событие нажатия клавиши <Enter> в поле редактора Edit2 (Искать что) с процедурой щелчка по кнопке Button2. Выделим все содержимое обработчика щелчка на 5utton2 (т. е. нашу процедуру поиска) в отдельную процедуру, которую назовем searchFiie. Чтобы не редактировать в ней все строки, где встречаются наименования компонентов, в начале процедуры придется добавить инструкцию with Formi do begin, а перед ее концом поставить дополнительный end. А процедура обработчика onclick для кнопки теперь будет выглядеть так:

procedure TForml.Button2Click(Sender: TObject);

/Искать1)

begin

SearchFiie; end; (Button2j

И создадим обработчик по нажатию клавиши (onKeyDown) для редактора

Ed i 12:

procedure TForml.Edit2KeyDown{Sender: TObject; var Key: Word; Shift: TShiftState);

begin (при нажатии Enter в поле редактора)

if Key=vk_Return then SearchFile; end;

Чтобы сделать совсем комфортно, свяжем процедуру по нажатию клавиши в поле редактора Editl (Искать где) также с этим обработчиком (напоминаю, что это делается выбором из выпадающего списка на закладке Event).

Теперь приступим к реализации нашей функции поиска. Сначала надо проверять, что именно записано в Edit2 — а вдруг там ничего нет, или одна буква, или одни пробелы? Если поразмыслить, то и двухбуквенные слова также не имеет смысла искать— значимых двухбуквенных слов очень мало, мы сделаем так, что в случае чего их можно будет задать в компании с каким-то другим словом при поиске по "Любому из слов". Кроме того, если в строке поиска задано всего одно слово, то функция "Любое из слов" теряет смысл, и мы тогда будем принудительно переводить программу в режим поиска "Фразу целиком". С учетом всего сказанного, в начале процедуры поиска (теперь это SearchFile) запишем:

(проверяем строку Edit2:j зtFind:=Edit2.Text;

if stFind=” then begin Edin2.SetFocus; exit end; stFind:=Trim(stFind); (удаляем возможные пробелы по концам) if length(stFind)<3 then begin Edit2.SetFocus; exit end;

(одно- и двухбуквенные слова не ищем) if post’ ‘, stFind)=0 then Radio8uttonAND.Checked:=True; (если слово только одно, то поиск только по AUDI

Гениально! Но до собственно поиска мы гак и не добрались, так что двинемся дальше. В список глобальных переменных добавим переменные stfile, stfind и sttext типа string, а в самый конец процедуры ReadFiieFomat добавим строку:

result:=FindString; (поиск строки)

Теперь напишем саму функцию FindString, она окажется довольно большой:

function FindStri.ng:boolean; (поиск строки в файле/ var i,j,n:integer;

var ast: array [0..99] of string; (до 100 слов no CR)

var sttemp:string;

begin

result:=False; не надейтесь, что найдем} assignfile(fd,fname);

reset(fd,1); (проверки не надо – уже открывали} stFile:=’

while not eof(fd) do begin

blockread(fd,xs,1); (по одному байту} if xs>31 then (только для символов} begin

if (st=’ KOI-8") or (st=’ cp866’l then (перекодировка)

if xs>127 then

begin

if nx=koi3 then nsl:=koi[xs]; (номер символа в KOI} if nx=altn then nsl:=alt[xs]; (номер символа в Alt} for i:=128 to 255 do (обратная кодировка, ищем символ) if win[i]=nsl then break; (нашли его код no Minl251) xs:=i; end;

stFile:=stFile+chr(xs); /имеем строку с содержимым файла} end; end;

closefile(fd);

if Forml.RadioButtonAND.Checked then (ищем no AND) begin

n:=pos(AnsiUpperCase(stFind),AnsiUpperCase(stFile)); (от регистра не должно зависеть) if n=0 then exit; (ничего не нашли} result:=True;

(нашли и формируем фрагмент текста с найденной строкой:) stText:=copy(stFile,n,100) ; while pos(‘<‘,stText)<>0 do begin

if pos(‘>’,stText)<>0 then (удаляем возможные HTML-Tent) delete(stText,pos(‘<‘,stText),abs(pos(‘>’,stText)-

pos(‘<‘,stText))+l) else delete(stText,pos(‘<‘, stText), 1) ; end;

{выделяем его жирным:} insert(‘</B>’,stText,length(stFind)+1) ; stText:='<B>’+stText+'<BR>'; end; /AND;

if Forml.RadioButtonOR.Checked then {ищем no ORJ begin i:=0;

sttemp:=stFind; /чтобы строку не испортить к следующему разу) while pos Г ‘,sttemp)<>0 do {разбираем sttemp на слова) begin

ast[i]:=сору(sttemp,1,pos(‘ sttemp)-1); {копируем слово) if (length(ast[i])<2) then continue;

{однобуквенные слова игнорируем) i:=i+l; if i=98 then break;

delete (sttemp, 1, pos (‘ ‘,sttemp)); {удаляем до пробела) sttemp:=TrimLeft (sttemp) ; {пробелов может быть много) end;

ast[i]:=sttemp; i:=i+l; stText:=’*;

for j:="0 to i do {ищем по очереди) begin

n:=pos(AnsiUpperCase(ast[j]),AnsiUpperCase(stFile)); if n=0 then continue; {не нашли – ищем следующее) result:=True; {нашли no крайней мере одно и формируем фрагмент текста с найденным словом;) sttemp:=copy(stFile,n, 100); while pos(‘<‘,sttemp)<>0 do begin

if pos(•>’,sttemp)<>0 then {удаляем возможные HTML-теги) delete(sttemp,pos(‘<‘,sttemp),abs(pos(‘>’,sttemp)-

pos(‘<‘,sttemp))+l) else delete(sttemp,pos(‘<‘,sttemp), 1); end;

/наделяем его жирным:)

insert(•</B>’,sttemp,length(ast[j])+1);

sttemp: ='<B> *+st temp+'<BR>';

stText:=stTexc+sttewp; {нашли и добавили к тексту) end; end; (CR)

stFile:=’*; {освобождаем память) end;

В этой функции мы сначала читаем файл целиком побайтно (о ужас! как будто нам не хватало чтения с диска при определении кодировки) и загружаем в строку символы, перекодируя их в соответствии с ранее определенной кодировкой. Есть сильное искушение ускорить этот процесс, и читать большими кусками, благо процедура biockread это позволяет. Но во избежание всяких неприятностей, мы не должны допустить, чтобы в строке случайно оказался символ с нулевым значением, а также некоторые другие управляющие символы с кодами, меньше 32, иначе программа может просто рухнуть. И вместо того, чтобы потом анализировать строку, "выковыривая" из нее ненужные символы, я их таким образом сразу удаляю. Конечно, неплохо бы коды 10+I.1 (перевод строки) и 12 (перевод страницы) заменять на пробел (мы ниже п процедуре выводим фрагменты текста для сведения), но мы оставим это на потом, ког да будем оптимизировать чтение с диска и там это можно будет сделать проще и быстрее. Отметим, что определять текущую кодировку нужно именно по строке с ее названием, а не по значению пх, т. к. определение по условию (nx=koi8) or (nx=alt_n) будет некорректным в случае, если ни одного символа со значением больше 127 не встретилось, кодировка определилась, как ASCII, и все три величины (пх, koi8 и aitn) одинаково равны нулю.

После того как содержимое файла (наконец-то!) окажется в строке (в кодировке Win 1251), мы начинаем собственно поиск. Для варианта поиска строки в режиме "Фразу целиком" все просто — мы ищем первое вхождение заданной строки, и, если его нет, выходим из обеих процедур последовательно с возвращаемым значением False. Если же строка находится, то мы формируем из текста файла фрагмент в 100 знаков (строка stText), начинающийся с искомой строки, при этом выделяем ее тегами жирного шрифта. На всякий слу чай мы заранее удаляем из этого фрагмента возможные HTML-теги, которые нам могут навредить в дальнейшем при отображении. Отметим про себя, что тут есть неоднозначность, заключенная в том, что мы можем задать в поиске именно HTML-тег, и тогда мы получим фрагмент без него — все подобные мелочи надо записывать, чтобы при дальнейшем усовершенствовании программы сделать действительно более продвинутый вариант, а не новую версию "для галочки".

Гораздо сложнее устроена процедура поиска отдельных слов (по "OR"). Сначала мы разбираем строку поиска на отдельные слова, причем не очень хорошо, что мы делаем это каждый раз заново— в принципе можно и делать это один раз в самом начале. Но я учел, что процедура разбора строки даже п 100 слов (максимальная емкость нашего массива) все равно протекает много быстрее, чем наше многократное чтение файла и последующий поиск вхождения, и решил не усложнять программу — чем меньше запуганных условий, тем меньше вероятность ошибки. Потом мы ищем вхождение каждого слова в отдельности и формируем общий текстовый фрагмент для вывода. В конце всей процедуры мы на всякий случай обнуляем строку stFiie. в которой содержится текст из файла — лучше, если по окончании процедуры или при ее прерывании по нажатию кнопки Отмена в памяти не останется большого массива символов.

Для того чтобы показать сформированный в stFiie фрагмент, мы модернизируем процедуру createHTMLtext, Добавив туда строку write (rtemphtni, StText J.

Чтобы она не выводилась в конце поиска, по окончании цикла в процедуре SearchFile (перед ПОСЛСДНИМ ВЫЗОВОМ createHTMLtext:) МЫ ВКЛЮЧаем строку stText: = ";. Для законченности придадим проекту номер версии 1.0 (и внесем это в заголовок формы: гюиск файлов i.o), и установим название проекта "Trace".

Рис. 14.2. Результаты работы программы Trace при чтении файлов с диска

Результаты поиска слов "Delphi" и "процедура" в большой папке с многоуровневыми вложенными каталогами и общим количеством файлов более 19 тыс. показаны на рис. 14.2. Как видите, процесс протекал довольно долго, почти 45 минут (Athlon ХР 1,7 ГГц, 512 Мбайт памяти, диск 7200 об/мин), и в связи с этим просто напрашивается установка еще одной кнопки, которая бы приостанавливала процесс без его отмены, чтобы можно было спокойно рассмотреть промежуточные результаты. Конечно, такое возможно, но реализуется очень непросто— нужно по этой кнопке запомнить все промежуточное состояние поиска (номер папки в списке, имя файла, на котором остановились, и т. п.), а потом еще как-то умудриться запустить это дело заново с нужного места. Указав таким образом возможный путь решения проблемы, я опять же оставлю это на откуп читателям — вы увидите, что в дальнейшем.

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

Если вы немного погоняете программу, то обнаружите, что она заметно тормозит систему — загрузка памяти, правда, асего от 10 до 25% (причем с программой, запущенной в отладочном режиме из Delphi), зато загрузка процессора на все 100%! Должен вас разочаровать— на самом деле процессор так загружен отнюдь не анализом строк, а всякой чепухой, вроде взаимодействия с драйверами жесткого диска. Но, кроме того, сама программа очень медленно реагирует на всякие операции типа сворачиаания и восстановления окна. Несколько улучшить положение можно, если расставить вызовы Application. ProcessMessages внутри всех критичных ЦИКЛОВ— чтения с Диска и поиска вхождения строк. Избежать "тормоза" вообще, в принципе, здесь не выйдет— посмотрим, что получится у нас потом, после обещанной модернизации.

Но перед тем как приступить, наконец, к ней, мы сделаем еще четыре вещи.

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

По теме:

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