Пропустить навигацию.
Главная

Диалоговые окна для поиска и замены текста

Приложение 6_15 (VB).

Объектами для поиска (FindDialog), поиска и замены (ReplaceDialog) располагают только системы BCB и Delphi. Функции их узко специализированы, и работают эти объекты только с текстами, находящимися на поле компонент RichEdit. Приведенный ниже текст приложения представляет собой слегка переработанный пример из help-файла системы BCB. Для его функционирования на форме должны быть расположены объекты RichEdit1, Button1 и FindDialog1. В составе приложения содержится всего два обработчика событий OnClick для кнопки и OnFind для диалога поиска.

После запуска приложения на поле редактора RichEdit1 надо набрать какой-нибудь текст и переместить курсор в начало текста. Затем нужно щелкнуть по кнопке с надписью <Начать поиск> для инициализации диалогового окна FindDialog1. На поле ввода открывшегося окна набирается поисковый фрагмент, который может содержать более одного слова. Запуск процедуры поиска начинается после щелчка по кнопке <Найти далее>, в результате которого возникает событие OnFind, и управление передается второму обработчику.

Содержимое поля редактора используется как одна длинная строка (RichEdit1->Text), в которой поиск начинается либо с самого первого символа (from=0), либо с позиции, следующей за ранее найденным выделенным фрагментом, если таковой существует. Переменная len хранит количество символов, оставшихся до конца длинной строки, и вместе с начальной позицией from определяет область поиска.

При нажатии кнопки <Найти далее> поисковый образ из окна ввода переносится в свойство FindDialog1->FindText. Собственно поиск осуществляет метод FindText, принадлежащий классу TRichEdit. Первый аргумент этого метода представляет поисковый образ, второй и третий аргументы задают область поиска. Четвертый аргумент представляет собой множество, элементы которого уточняют специфику поиска. Единственный член этого множества, использованный в нашем примере (stMatchCase), предписывает при сравнении текстов игнорировать разницу между большими и малыми буквами. К сожалению, эта возможность не распространяется на русскоязычные тексты. Если поисковый образ обнаружен в заданной зоне, то метод возвращает номер первой позиции найденного фрагмента. В противном случае возвращается -1. Обратите внимание на передачу фокуса RichEdit1->SetFocus(). Пока мы набирали искомое слово и щелкали по кнопке, в центре внимания операционной системы находилось диалоговое окно FindDialog. Но для того, чтобы подсветить найденный текст, надо не только установить новые значения свойств SelStart и SelLength, но и активизировать объект RichEdit1. На рис. 6.20 приведен момент работы приложения.

void __fastcall TForm1::Button1Click(TObject *Sender)

{

FindDialog1->Execute();

}

//------------------------------------------------------------------

void __fastcall TForm1::FindDialog1Find(TObject *Sender)

{

int from=0, len, pos;

if(RichEdit1->SelLength != 0)

from = RichEdit1->SelStart + RichEdit1->SelLength;

len = RichEdit1->Text.Length() - from;

pos = RichEdit1->FindText(FindDialog1->FindText,from,len,

TSearchTypes()<< stMatchCase);

if(pos!=-1)

{ RichEdit1->SetFocus();

RichEdit1->SelStart=pos;

RichEdit1->SelLength=FindDialog1->FindText.Length();

}

}

Стандартные диалоговые объекты

Рис. 6.20. Объект FindDialog в работе

Диалоговое окно ReplaceDialog построено как расширенный вариант окна FindDialog. Однако с его помощью можно вести поиск и замену не только в полнофункциональных редакторах текста RichEdit, но и в более простых компонентах типа Memo. Ниже приводится и комментируется пример такого диалога, заимствованный из help-файла. На форме расположены компоненты Memo1, кнопка Button1 (инициатор появления окна диалога) и ReplaceDialog1. Диалоговое окно возникает на экране в результате обращения к методу Execute. Вообще говоря, обработчик события OnClick можно бы несколько расширить за счет указания позиции диалогового окна на экране. Например, разместив его под окном Memo1:

ReplaceDialog1->Pos=Point(Left+5,Top+40+Memo1->Heght);

Но не так уж и трудно вручную перетащить появившееся окно в более удобное место.

Обработчик события OnReplace получит управление после щелчка по одной из кнопок <Заменить> (Replace) или <Заменить все> (Replace All). Правда, до этого нужно разместить в поле Memo1 какой-либо текст и набрать в полях диалогового окна искомый и заменяющий его фрагмент (рис. 6.22). Обратите внимание на способ приведения типа объекта Sender, пославшего сообщение OnReplace и имеющего тип TObject*, к типу указателя dlg, выполненное по классическому стандарту языка Си.

Приложение 6_16 (BCB).

Для поиска фрагмента, подлежащего замене, используется метод Pos, примененный к содержимому Memo1 (Memo1->Lines->Text). Если искомый фрагмент обнаружен, то производится выделение найденного текста (Memo1->SelText) путем формирования начального индекса в строке (SelStart) и длины выделенного текста (SelLength), совпадающей с длиной поискового образа. После этого выделенный фрагмент заменяется на заданное значение ReplaceText. Если поисковый образ не обнаружен (функция Pos возвратила нулевое значение), выдается короткий звуковой сигнал.

void __fastcall TForm1::Button1Click(TObject *Sender)

{

ReplaceDialog1->Execute();

}

//-------------------------------------------------------------------

void __fastcall TForm1::ReplaceDialog1Replace(TObject *Sender)

{

TReplaceDialog *dlg = (TReplaceDialog *)Sender;

int pos = Memo1->Lines->Text.Pos(dlg->FindText);

if (pos > 0)

{

Memo1->SelStart = pos - 1;

Memo1->SelLength = dlg->FindText.Length();

Memo1->SelText = dlg->ReplaceText;

}

else

MessageBeep(0);

}

Для управления деталями поиска и замены можно использовать как кнопки и пометки в диалоговом окне, так и значения подсвойств комплексного свойства Option. Среди них параметры, задающие направление поиска (frDown), поиск отдельного слова (frWholeWord) или произвольного вхождения (frDisableWholeWord), указание об игнорировании или учете разницы между большими и малыми буквами (frMatchCase, frDisable MatchCase), необходимость одноразовой (frReplace) или многократной (frReplaceAll) замены и др. Каждое из таких свойств принимает одно из двух логических значений, устанавливая одновременно правильное значение противоположного по смыслу свойства.

На рис. 6.21 и 6.22 представлены кадры работы приложения до и после замены фрагмента «bbb ccc» на тройку символов «666».

Стандартные диалоговые объекты

Рис. 6.21. Диалоговое окно перед началом замены

Стандартные диалоговые объекты

Рис.6.22. Редактируемый текст после однократной замены

Приложение 6_17 (BCB).

Функция Pos может быть с успехом использована не только в обработчике события OnReplace, но и в обработчике события OnFind. И это дает возможность производить поиск не только в продвинутых редакторах типа RichEdit, но и в более простых объектах типа Memo. Однако здесь потребуется небольшая хитрость. Дело в том, что при поиске и замене найденный фрагмент заменяется указанным текстом и при продолжении замен поиск в строке Memo->Lines->Text можно начинать с начала строки. При просто поиске потребуется пропускать ранее найденные вхождения. Поэтому надо контролировать позицию предыдущего вхождения, и продолжать поиск вслед за очередным выделенным фрагментом. В этом нам поможет глобальная переменная len, предназначенная для хранения длины уже обработанной части строки, и локальная переменная q, в которую каждый раз переносится необработанный хвост. Ниже приводится текст приложения, которое может искать вхождения как в объектах RichEdit, так и в записных книжках типа Memo. Для его работы существенно то, что при инициализации поиска в глобальную переменную len заносится 0.

int len; //глобальная переменная

void __fastcall TForm1::Button1Click(TObject *Sender)

{

len=0;

FindDialog1->Execute();

}

//-------------------------------------------------------------------

void __fastcall TForm1::FindDialog1Find(TObject *Sender)

{

AnsiString q=RichEdit1->Lines->Text.Delete(1,len);

TFindDialog *dlg = (TFindDialog *)Sender;

int pos = q.Pos(dlg->FindText);

if (pos > 0)

{

RichEdit1->SelStart = len+pos-1;

len=RichEdit1->SelStart+dlg->FindText.Length();

RichEdit1->SelLength = dlg->FindText.Length();

RichEdit1->SetFocus();

}

else MessageBeep(0);

}

Приложение 6_18 (Delphi).

Аналогичный вариант для Delphi выглядит следующим образом:

var

Form1: TForm1;

len : integer; {глобальная переменная}

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);

begin

len:=0;

FindDialog1.Execute;

end;

procedure TForm1.FindDialog1Find(Sender: TObject);

var

j,p:integer;

q1,q:String;

begin

p:=length(Memo1.Lines.Text);

q1:=FindDialog1.FindText;

{перенос хвоста строки в q}

for j:=len+1 to p do

q:=q+Memo1.Lines.Text[j];

{поиск вхождения q1 в q}

p:=Pos(q1,q);

if p>0 then begin

Memo1.SelStart:=len+p-1;

len:=Memo1.SelStart+Length(FindDialog1.FindText);

Memo1.SelLength:=Length(FindDialog1.FindText);

Memo1.SetFocus; end

else MessageBeep(0);

end;

end.

От переменной с именем pos здесь пришлось отказаться, т.к. Object Pascal в отличие от C++ не делает разницы между большими и малыми буквами, и имя переменной начинало конфликтовать с функцией Pos. Вместо нее использована переменная p. Во-вторых, вместо удаления обработанного начала длинной строки (функция Delete в предыдущем примере) пришлось переносить необработанный хвост посимвольно. В системе Delphi функция Delete предназначена для другой цели — она удаляет строку с указанным номером из Memo.