Учись программированию на C++ Builder бесплатно!

Компоненты FindDialog и ReplaceDialog, вызывающие диалоги поиска и замены фрагментов текста, очень похожи и имеют одинаковые свойства, кроме одного, задающего заменяющий текст в компоненте ReplaceDialog. Такое сходство не удивительно, поскольку ReplaceDialog - производный класс от FindDialog.

FindDialog – диалог поиска текста

ReplaceDialog – диалог замены текста

Компоненты имеют следующие основные свойства:

Название Значение
FindText Текст, заданный пользователем для поиска или замены. Программно может быть установлен как начальное значение, предлагаемое пользователю.
ReplaceText Только в компоненте ReplaceDialog - текст, который должен заменять FindText.
Position Позиция левого верхнего угла диалогового окна, заданная типом TPoint - записью, содержащей поля X (экранная координата по горизонтали) и Y (экранная координата по вертикали).
Left Координата левого края диалогового окна, то же, что Position.X.
Top Координата верхнего края диалогового окна, то же, что Position.Y.
Options Множество опций.

Последний параметр Options может содержать следующие свойства:

Название Значение
frDisableMatchCase Делает недоступным индикатор С учетом регистра в диалоговом окне.
frDisableUpDown Делает недоступными в диалоговом окне кнопки Вверх и Вниз группы Направление, определяющие направление поиска.
frDisableWholeWord Делает недоступным индикатор Только слово целиком в диалоговом окне.
frDown Выбирает кнопку Вниз группы Направление при открытии диалогового окна. Если эта опция не установлена, то выбирается кнопка Вверх.
frFindNext Эта опция включается автоматически, когда пользователь в диалоговом окне щелкает на кнопке Найти далее, и выключается при закрытии диалога.
frHideMatchCase Удаляет индикатор С учетом регистра из диалогового окна.
frHideWholeWord Удаляет индикатор Только слово целиком из диалогового окна.
frHideUpDown Удаляет кнопки Вверх и Вниз из диалогового окна.
frMatchCase Этот флаг включается и выключается, если пользователь включает и выключает опцию С учетом регистра в диалоговом окне. Можно установить эту опцию по умолчанию во время проектирования, чтобы при открытии диалога она была включена.
frReplace Применяется только для ReplaceDialog. Этот флаг устанавливается системой, чтобы показать, что текущее (и только текущее) найденное значение FindText должно быть заменено значением ReplaceText.
frReplaceAll Применяется только для ReplaceDialog. Этот флаг устанавливается системой, чтобы показать, что все найденные значения FindText должны быть заменены значениями ReplaceText.
frShowHelp Задает отображение кнопки Справка в диалоговом окне.
frWholeWord Этот флаг включается и выключается, если пользователь включает и выключает опцию Только слово целиком в диалоговом окне. Можно установить эту опцию по умолчанию во время проектирования, чтобы при открытии диалога она была включена.

Сами по себе компоненты FindDialog и ReplaceDialog не осуществляют ни поиска, ни замены. Они только обеспечивают интерфейс с пользователем. А поиск и замену надо осуществлять программно. Для этого можно пользоваться событием OnFind, происходящим, когда пользователь нажал в диалоге кнопку Найти далее, и событием OnReplace, возникающим, если пользователь нажал кнопку Заменить или Заменить все. В событии OnReplace узнать, какую именно кнопку нажал пользователь, можно по значениям флагов frReplace и frRepIaceAll.

Поиск заданного фрагмента в компоненте RichEdit легко проводить, используя его метод FindText, объявленный следующим образом:

int __fastcall FindText(const System::AnsiString SearchStr, int StartPos, int Length, TSearchTypes Options);

Этот метод ищет в тексте RichEdit фрагмент, заданный параметром SearchStr. Поиск производится, начиная с позиции StartPos (позиция первого символа текста считается нулевой), на протяжении Length символов. Параметр Options является множеством, которое может содержать элементы stWhoIeWord (поиск только целого слова) и stMatchCase (поиск с учетом регистра). Метод возвращает позицию найденного вхождения. Если заданный фрагмент не найден, возвращается -1.

Ниже приведен код, осуществляющий поиск заданного фрагмента в тексте компонента RichEdit1. Вызвать диалог поиска можно операторами:

/* начальное значение текста поиска - текст, выделенный в RichEdit1 */
FindDialog1->FindText = RichEdit1->SelText;
FindDialog1->Execute();

Они задают в качестве начального значения для поиска текст, выделенный в окне RichEdit1, и затем вызывают диалог поиска FindDialog1. Обработчик события OnFind компонента FindDialog1 может иметь вид:

void __fastcall TForm1::FindDialog1Find(TObject *Sender)
{
int FoundAt, StartPos, ToEnd;
TSearchTypes Option;
/* если было выделение, то поиск идет, начиная с его последнего символа, иначе с позиции курсора */
StartPos = RichEdit1->SelStart;
if(RichEdit1->SelLength)
StartPos += RichEdit1->SelLength;
/* ToEnd - длина текста, начиная с первой позиции поиска и до конца */
ToEnd = RichEdit1->Text.Length() - StartPos;
/* поиск целого слова или нет в зависимости от установки пользователя */
if(FindDialog1->Options.Contains(frWholeWord))
Option << stWholeWord;
else Option >> stWholeWord;
/* поиск с учетом или без учета регистра в зависимости от установки пользователя */
if(FindDialog1->Options.Contains(frMatchCase))
Option << stMatchCase;
else Option >> stMatchCase;
FoundAt = RichEdit1->FindText(FindDialog1->FindText, StartPos, ToEnd, Option);
if(FoundAt != -1) // если найдено
{
RichEdit1->SetFocus();
RichEdit1->SelStart = FoundAt;
RichEdit1->SelLength = FindDialog1->FinaText.Length();
}
else ShowMessage("Текст '" + FindDialog1->FindText + "' не найден");
}

Функция FindDialog1Find срабатывает, когда пользователь нажал в диалоге кнопку Найти далее. Комментарии в тексте этой функции поясняют этапы поиска. Сначала производится установка области текста (переменные StartPos и ToEnd), в которой проводится поиск. Затем устанавливаются атрибуты поиска формируется множество Option в зависимости от установленных пользователем опций. Затем методом FindText проводится сам поиск. Если нового вхождения искомого текста не найдено (метод FindText вернул -1), то пользователю выдается сообщение об этом с помощью функции ShowMessage.

Приведенный пример относился к поиску в компоненте RichEdit. Для организации поиска в тексте компонента Memo удобно использовать метод Pos класса AnsiString, который объявлен следующим образом:

int __fastcall Pos(const AnsiString& subStr) const;

Метод возвращает индекс первого символа первого вхождения подстроки subStr в строку, к которой применяется этот метод. Индексы начинаются с 1. Если subStr не содержится в строке, то возвращается 0.

Для организации поиска потребуются еще две функции класса AnsiString: SubString и LowerCase. Первая из них определена как:

AnsiString __fastcall SubString(int index, int count) const;

Она возвращает подстроку, начинающуюся с символа в позиции index и содержащую count символов.

Функция LowerCase, определенная как:

AnsiString __fastcall LowerCase() const;

возвращает строку символов S, переведенную в нижний регистр.

Теперь можно рассмотреть пример организации поиска. Пусть в приложении имеется компонент Memo1 и при выборе раздела меню MFind нужно организовать поиск в тексте, содержащемся в Memo1. Для упрощения задачи исключим опцию поиска только целых слов и опцию поиска вверх от положения курсора. Программа, реализующая поиск, может иметь следующий вид:

void __fastcall TForm1::MFindClick(TObject *Sender)
{
/* начальное значение текста поиска - текст, выделенный в Memo1 */
FindDialog1->FindText = Memo1->SelText;
FindDialog1->Execute();
}
//-----------------------------
void __fastcall TForm1::FindDialog1Find(TObject *Sender)
{
int FoundAt, StartPos, ToEnd;
/* если было выделение, то поиск идет, начиная с его последнего символа, иначе с позиции курсора */
StartPos = Memo1->SelStart;
if(Memo1->SelLength)
StartPos += Memo1->SelLength;
/* ToEnd - длина текста, начиная с первой позиции поиска и до конца */
ToEnd = Memo1->Text.Length() - StartPos;
/* поиск с учетом или без учета регистра в зависимости от установки пользователя */
if(FindDialog1->Options.Contains(frMatchCase))
FoundAt = StartPos + Memo1->Text.SubString(StartPos+1, ToEnd).Pos(FindDialog1->FindText);
else
FoundAt = StartPos + Memo1->Text.SubString(StartPos+1, ToEnd).LowerCase().Pos(FindDialog1->FindIext.LowerCase());
if(FoundAt != StartPos) // если найдено
{
Memo1->SetFocus();
Memo1->SelStart = FoundAt-1;
Memo1->SelLength = FindDialog1->FindText.Length();
}
else ShowMessage("Текст '" + FindDialog1->FindText + "' не найден");
}

Программа аналогична приведенной ранее для компонента RichEdit и отличается только несколькими операторами, осуществляющими непосредственно поиск. 

При реализации команды Заменить приведенные выше процедуры можно оставить теми же самыми, заменив в них FindDialog1 на ReplaceDialog1. Дополнительно можно написать процедуру обработки события OnReplace компонента ReplaceDialog1. Кроме того желательно обеспечить, чтобы при нажатии пользователем в диалоге клавиши Заменить все программа просматривала бы весь текст и проводила все замены без дополнительных вопросов пользователю. Для этого можно в конце обработчика события OnFind вставить оператор, который в случае, если пользователь нажал в диалоге клавишу Заменить все, вызывал бы обработчик события OnReplace. В итоге текст, обеспечивающий замену в компоненте RichEdit, может иметь вид:

void __fastcall TForm1::ReplaceDialog1Find(TObject *Sender)
{
// Если нажата кнопка "Заменить все", то уход на замену
if(ReplaceDialog1->Options.Contains(frReplaceAll))
ReplaceDialog1Replace(Sender);
}
//-----------------------------
void __fastcall TForm1::ReplaceDialog1Replace(TObject *Sender)
{
if(RichEdit1->SelText != "") // Если есть выделенный текст Замена выделенного текста
RichEdit1->SelText = ReplaceDialog1->ReplaceText;
else if(ReplaceDialog1->Options.Contains(frReplace))
{
ShowMessage("Текст '" + ReplaceDialog1->FindText + "' не найден");
return;
}
// Если нажата кнопка "Заменить все", то уход на поиск
if(ReplaceDialog1->Options.Contains(frReplaceAll))
ReplaceDialog1Find(Sender);
}

В функции ReplaceDialog1Find в конце поиска очередного вхождения искомого фрагмента в текст проверяется нажатие пользователем кнопки Заменить все. Если она нажата, то происходит обращение к функции ReplaceDialog1Replace, в которой осуществляется замена текста, после чего сразу автоматически опять вызывается функция ReplaceDialog1Find. Таким образом без остановок просматриваются и заменяются все вхождения искомого фрагмента в текст. Если же кнопка Заменить все не нажата, то программа останавливается и ждет, пока пользователь нажмет в диалоге кнопку Найти далее или Заменить. Если нажимается кнопка Найти далее, то производится следующий вызов ReplaceDialog1Find. А при нажатии кнопки Заменить вызывается функция ReplaceDialog1Replace, которая заменяет выделенный текст.

Приведенные коды рассчитаны на компонент RichEdit. Для компонента Memo в них просто надо заменить RichEdit1 на Memo1.

Поделиться