Delphi. Урок 4.3. Продолжаем писать текстовый редактор (локальные переменные в Delphi). |
Автор megabax | ||||
22.06.2009 г. | ||||
Продолжаем писать текстовый редактор (локальные переменные в Delphi).
В предыдущей статье я описал, как сохранить отредактированный текст в файла, а так же рассказал о некоторых возможностях TForm. Теперь пришло время наполнить содержимым ветку меню «Редактирование». Занесем туда новый пункт меню «Поиск и замена» идентификатором itReplace. Теперь положим на форму диалог поиска и замены*.
В обработчик OnClick пункта меню itReplace добавим строку:
rdReplaceDialog.Execute;
Все, теперь при выборе этого пункта будет запускаться вот такой диалог*:
Правда, пока он у нас еще ничего не дает. Можно хоть сколько жать на «Найти далее» или «Заменить», ничего не произойдет. Поэтому, нам нужно написать обработчики события для диалога поиска и замены. Начнем с OnFind. Вместо Пары begin end нам нужно поместить вот такой текст:
Несколько пояснений к тексту программы. Оператором «var» мы объявляем локальные переменные, которые действуют только в пределах данной процедуры. Есть еще глобальные переменные, которые действуют в пределах всей программы. Их изменение в любом месте приведет к изменению именно этих переменных. А вот если в теле подпрограммы объявить локальную переменную с таким же именем, как и глобальная, то, изменяя эту переменную, мы глобальную не меняем. В качестве аналогии возьмем имена людей. Пусть в некотором классе учиться мальчик Вова. В другом классе тоже учится мальчик Вова. Но это два совершенно разных человека (локальные переменные). А еще есть в этой школе трудный ученик Вова, которого знают все. Этот общеизвестной Вова – глобальная переменна. Поэтому, когда в классе нету Вовы, то если кто нибудь заведет речь о Вове, то все будут знать, кого имеют в виду. А как быть в том случае, когда речь о Вове идет в классе, где уже есть Вова? Правильно, все будут думать про того Вову, который уже учиться в этом классе. Точно так же и с локальным переменными. Сразу возникает вопрос, как быть, когда нужно сказать не про того Вову, который учиться в нашем классе, а про другого, которого знает вся школа? Очень просто, нужно просто указать в разговоре, что речь идет о Вове, которого знает вся школа. Точно так же мы поступим и в программе. Укажем перед переменной имя модуля, в котором она объявлена. Но пока мы не будем заворачиваться над этим вопросов, у нас еще программа не протестирована. Давайте запустим ее и проверим, действительно ли работает поиск.
Как выяснилось, поиск у нас работает только на первое вхождение. А вот дальше, сколько не жми «Найти далее», все равно выделенным остается только первым найденный фрагмент, хотя, как мы видим из скриншота, слов «Регистр», который, собственно говоря мы ищем, в тексте несколько. Что бы исправить эту ошибку, разберемся для начала, как вообще работает программа. И так, что такое begin, вы наверное уже догадались, это оператор означает начало блока программы. Далее идет for. Это цикл со счетным количество повторений. В данном случае после каждого прохода цикла переменная I увеличивается на единицу, начиная от 0 кончая значением moText.Lines.Count. Поясню подробно конструкцию moText.Lines.Count. Сначала у нас идет имя объекта moText. Через точку мы обращаемся к его свойству «Lines». Самое это свойство так же является объектом и имеет свойства. К ним мы тоже обращается через точку. После конструкции for … do у нас идет пара begin … end. Между ними блок кода, который будет выполнятся столько раз, сколько будет бежать цикл. Разберем этот блок подробно:
PosReturn := Pos(rdReplaceDialog.FindText,moText.Lines[I]);
Этой командой (Pos) мы ищем вхождение куска текста в другой текст. В частности, искомая строка rdReplaceDialog.FindText – это текст, который мы ввели в диалоге. Текст, в котором ищем – очередная строка текстового поля moText. Если мы ничего не нашли, то функция вернет 0. Поэтому следующая команда это структура if… then – оператор условия (ветвления). Если результат поиска у нас не нулевой (знак «<>» означает «не равно»), то выполняем блок, идущий после then и заключенный между begin end.
Этот блок делает следующее:
1. Вычисляет количество символов до строки, в которой найдена искомая комбинация символов. Это делают операторы
SkipChars := 0; for J := 0 to I - 1 do SkipChars := SkipChars + Length(moText.Lines[J]);
В частности, после до не стоит begin end, следовательно, в цикле выполняется только одна команда SkipChars := SkipChars + Length(moText.Lines[J]);, которая просто увеличивает содержимое переменной SkipChars на длину очередной строки (функция Length).
2. Затем мы добавляем к содержимому этой переменной количество байтов, содержащиеся в маркерах конца строки. Это делают вот такие строки.
SkipChars := SkipChars + (I*2);
Байт соответствует одному символу из 256-ти. Этим символом может быть буква, цифра, а так же специальный, невидимый символ, обозначающий определенной действие, например. В многострочной тексте каждая строка заканчивается одним двумя такими символами, а именно, возврат каретки и перевод строк.
3. Далее, мы прибавляем количество символов с начала строки до искомой комбинации
SkipChars := SkipChars + PosReturn - 1;
4. И, уже после того, как вычислили номер символа, с которого начинается искомый фрагмент текста, помещаем этот фрагмент текста как выделенный:
moText.SetFocus; moText.SelStart := SkipChars; moText.SelLength := Length(rdReplaceDialog.FindText);
5. Прерываем цикл командой break. Иными словами, как только мы нашли искомую комбинацию, нам больше не нужно крутить цикл, и он завершается, даже если не проделано заданное число повторений.
Соответственно, если ни в одной строке искомая комбинация не выполнена, то и вышеперечисленные действия так же не будут выполнятся.
Каким образом мы сделаем продолжение поиска? Ясно дело, нам нужно как то запоминать позицию поиска, что бы в следующий раз поиск начать с нее.
Немного модифицируем объявление класса формы (добавленные строки выделены красным)
Текст, который мы добавили
FI, FPosReturn:integer;
Означает объявления дополнительных полей класса формы. Это еще не свойства, а всего лишь внутренние переменные. В программе мы можем менять их значения. В них то как раз мы и будем запоминать позицию поиска. В обработчик события FormCreate нашей формы добавим строки: FI:=0; FPosReturn:=0;
Которые обнуляют наш счетчик поиска. Ну, а теперь изменим обработчик события OnFind нашего диалога поиска и замены:
И так, что же у нас поменялось. Поиск мы осуществляем не с нулевой строки, а стой, с которой закончили в прошлый раз:
for I := FI to moText.Lines.Count do
Если у нас первый поиск, то это как раз и будет нулевая строка.
Выражением
if FPosReturn=0 then s:=moText.Lines[I] else s:=copy(moText.Lines[I],FPosReturn+1,Length(moText.Lines[I])-FPosReturn+1);
Мы проверяем, первый ли это у нас поиск. Если да то берем для поиска всю строку, если нет – то только ее часть, начиная с символа, следующего за началом найденного в прошлый раз слова.
Командой
FPosReturn:=FPosReturn+PosReturn;
Мы запоминаем позицию поиска, а командой
FI:=I;
Запоминаем строку поиска.
Мы усовершенствовали наш текстовый редактор, заставив его искать не первое попавшее вхождение слов, а каждое. На самом деле его еще можно усовершенствовать, но об этом в следующей статье.
Скриншоты, помеченные знаком * , являются цитатами и иллюстрациями программного продукта "Delphi", авторское право на который принадлежит "Borland Software Corporation".
|
||||
Последнее обновление ( 01.07.2012 г. ) |
« След. | Пред. » |
---|