书籍:《Visual C++ 2017从入门到精通》的3.8.5 查找/替换对话框的使用
环境:visual studio 2022
内容:[例 3.14] 查找/替换对话框的使用
说明:以上内容大部分来自腾讯元宝。
1.新建对话框工程
资源视图->对话框->删除原有控件->在工具箱中找到Edit Control(途中CEdit是误写)并添加到对话框->设置属性“多行”和“想要返回”为True.
3.增加全局变量
CFindReplaceDialog* gpFindReplaceDlg = NULL; //指向查找对话框或替换对话框
BOOL gbLastCase = FALSE; //记录上一次的大小写情况
int gpos = 0, gcurpos; //gpos是查找索引;gcurpos是当前索引
CString gstrEdit, gstrLast; //gstrEdit存放上一次编辑框中的正文内容;gstrLast存放上一次查找框中的内容
4.添加“显示查找对话框”和“显示替换对话框”按钮,并分别添加点击按钮事件处理:
//该代码用于处理“查找”按钮的点击事件,实现非模态查找 / 替换对话框的初始化与显示。核心逻辑包括:
//
//懒加载对话框对象:首次点击时创建对话框实例,避免重复创建。
//配置对话框参数:设置查找方向、默认搜索内容等。
//显示对话框:确保对话框可见并响应用户操作。
void CTest314Dlg::OnBnClickedButton1() //查找按钮点击事件处理函数
{
// TODO: 在此添加控件通知处理程序代码
// 检查对话框对象是否已创建(避免重复实例化)
if (!gpFindReplaceDlg)
{
// 动态创建CFindReplaceDialog对象(必须使用new在堆上分配[1,2,7]))
gpFindReplaceDlg = new CFindReplaceDialog();
// 初始化对话框参数并显示
gpFindReplaceDlg->Create(
TRUE, // TRUE显示查找对话框,FALSE显示查找/替换对话框[1,7](@ref)
NULL, // 默认查找字符串(未设置时对话框为空)
NULL, // 默认替换字符串
FR_DOWN, // 搜索方向:向下(从光标位置向下查找)
this); // 父窗口指针,用于消息通知[3,7](@ref)
}
// 重置查找起始位置(可能用于多行文本控件的逐行查找)
gpos = 0;
// 显示对话框(非模态显示,允许与其他窗口交互)
gpFindReplaceDlg->ShowWindow(SW_SHOW);
}
//该代码用于处理“替换”按钮的点击事件,实现非模态查找 / 替换对话框的初始化与显示。核心逻辑包括:
//
//懒加载对话框对象:首次点击时创建对话框实例,避免重复创建。
//配置对话框参数:设置查找方向、默认查找 / 替换内容等。
//显示对话框:确保对话框可见并响应用户操作。
void CTest314Dlg::OnBnClickedButton2() // 替换按钮点击事件处理函数
{
// TODO: 在此添加控件通知处理程序代码
// 检查对话框对象是否已创建(避免重复实例化)
if (!gpFindReplaceDlg)
{
// 动态创建CFindReplaceDialog对象(必须使用new在堆上分配)
gpFindReplaceDlg = new CFindReplaceDialog();
// 初始化对话框参数并显示
gpFindReplaceDlg->Create(
FALSE, // FALSE表示显示替换对话框(TRUE为查找对话框)
m_FindString, // 默认查找字符串(从成员变量获取)
m_ReplaceString, // 默认替换字符串(从成员变量获取)
FR_DOWN, // 搜索方向:向下(从光标位置向下查找)
this); // 父窗口指针,用于消息通知
}
// 重置查找起始位置(可能用于多行文本控件的逐行查找)
gpos = 0;
// 显示对话框(非模态显示,允许与其他窗口交互)
gpFindReplaceDlg->ShowWindow(SW_SHOW);
}
5.注册系统预定义消息FINDMSGSTRING
//1.在.h文件中注册消息
static UINT WM_FINDREPLACE = ::RegisterWindowMessage(FINDMSGSTRING);
//2.在.h文件中DECLARE_MESSAGE_MAP()前声明消息处理函数
afx_msg LRESULT OnFindReplace(WPARAM wParam, LPARAM lParam);
//3.在.cpp中绑定消息
BEGIN_MESSAGE_MAP(CTest314Dlg, CDialogEx)
ON_REGISTERED_MESSAGE(WM_FINDREPLACE, &CTest314Dlg::OnFindReplace)
END_MESSAGE_MAP()
//4.实现消息处理函数
LRESULT CTest314Dlg::OnFindReplace(WPARAM wParam, LPARAM lParam)
{
...
}
6.详细介绍消息处理函数OnFindReplace()
问题:向上查找時定位不准,可能是因爲調用str.find_last_of(strFind, gpos);時候返回的返回 strFind 中任意字符在 str 中最后一次出现的位置。
//该函数是 CFindReplaceDialog 的回调函数,用于处理查找 / 替换操作的用户交互逻辑,核心功能包括:
//状态捕获:获取查找方向、大小写敏感等配置。
//查找逻辑:根据用户操作(查找 / 替换 / 全部替换)执行文本搜索。
//结果反馈:高亮显示匹配内容或提示未找到信息。
LRESULT CTest314Dlg::OnFindReplace(WPARAM wParam, LPARAM lParam)
{
BOOL bCase = gpFindReplaceDlg->MatchCase(); // 是否区分大小写
BOOL bDown = gpFindReplaceDlg->SearchDown(); // 搜索方向(向下/向上)
CString strRawFind;
//状态捕获与终止检查
if (gpFindReplaceDlg->IsTerminating()) // 检查对话框是否正在关闭
{
gpFindReplaceDlg = NULL; // 释放对话框指针
return 0;
}
//获取查找 / 替换内容
CString strFind = gpFindReplaceDlg->GetFindString(); // 查找字符串
int lenStrFind = strFind.GetLength();
CString strReplace = gpFindReplaceDlg->GetReplaceString(); // 替换字符串
int lenStrReplace = strReplace.GetLength();
TRACE(_T("lenStrReplace = %d.\n"), lenStrReplace);
CString strEdit;
m_edt.GetWindowText(strEdit); // 获取编辑框当前文本
//状态变更检测
//通过全局变量(gbLastCase, gstrEdit, gstrLast) 记录上一次状态,避免重复搜索时位置错误。
// 大小写模式变化时重置搜索位置
if (gbLastCase != bCase)
{
gbLastCase = bCase;
gpos = bDown ? 0 : strEdit.GetLength() - 1;
TRACE(_T("gpos a = %d \n"), gpos);
}
// 编辑框内容变化时重置搜索位置
if (gstrEdit.Compare(strEdit) != 0)
{
gstrEdit = strEdit;
gpos = bDown ? 0 : strEdit.GetLength() - 1;
TRACE(_T("gpos b = %d \n"), gpos);
}
// 查找字符串变化时重置搜索位置
if (gstrLast.Compare(strFind) != 0)
{
gstrLast = strFind;
gpos = bDown ? 0 : strEdit.GetLength() - 1;
TRACE(_T("gpos c = %d \n"), gpos);
}
// 查找逻辑实现
// 转换为统一大小写(若不区分大小写)
strRawFind = strFind;
if (!bCase)
{
strEdit.MakeUpper();
strFind.MakeUpper();
}
if (gpFindReplaceDlg->FindNext())
{
TRACE(_T("FindNext \n"));
if (bDown) // 向下查找
{
if (gpos == strEdit.GetLength() - 1)
{
MessageBox(_T("已经向下找到文件末尾,但没有找到"), _T("查找替换"),
MB_OK | MB_ICONINFORMATION);
gpos = 0; //reset gpos for loop searching.
goto end;
}
/*功能:在 strEdit 中从位置 gpos 开始查找子字符串 strFind。
返回值:
若找到,返回子字符串的起始索引(从 0 开始)。
若未找到,返回 - 1。*/
TRACE(_T("strEdit = %s \n"), strEdit);
TRACE(_T("gpos 1 = %d \n"), gpos);
gpos = strEdit.Find(strFind, gpos); // 从当前位置开始查找
TRACE(_T("gpos 2 = %d \n"), gpos);
if (gpos == -1)
{
gpos = strEdit.GetLength() - 1; //若未找到匹配项(gpos == -1),将 gpos 设置为文本末尾(Length - 1),可能用于后续向上查找的起始位置。
MessageBox(_T("无法找到") + strRawFind);
}
else
{
/*设置控件焦点
功能:将输入焦点设置到编辑控件 m_edt 上。
作用:
用户可直接在控件中输入或编辑文本。
视觉上控件边框高亮,提示当前活动对象。*/
m_edt.SetFocus(); // 设置焦点到编辑控件
/*设置选中文本范围
功能:选中编辑控件中从 gpos 开始、长度为 lenStrFind 的文本。
参数说明:
gpos:选中起始位置(通常为匹配子字符串的起始索引)。
gpos + lenStrFind:选中结束位置(起始位置 + 子字符串长度)。
作用:
高亮显示匹配内容,直观反馈查找结果。
光标自动定位到选中文本末尾(即 gpos + lenStrFind 处)。*/
TRACE(_T("gpos 1 = %d, gpos + lenStrFind = %d. \n"), gpos, gpos + lenStrFind);
m_edt.SetSel(gpos, gpos + lenStrFind); // 高亮显示匹配内容
/*记录当前匹配位置
功能:将当前匹配起始位置 gpos 保存到全局变量 gcurpos。
作用:
为后续替换操作提供基准位置(如替换时需知道选中文本的起始点)。
避免因光标移动或界面刷新导致位置丢失。*/
gcurpos = gpos; // 记录当前选中位置
TRACE(_T("gcurpos = %d \n"), gcurpos);
/*调整光标位置
功能:将 gpos 更新为匹配内容起始位置的前一个位置。
逻辑分析:
若原 gpos 为匹配子字符串的起始索引(如 5),更新后 gpos = 5 - lenStrFind。
示例:
若匹配子字符串长度为 3,则 gpos 更新为 2。
光标将停留在匹配内容的起始字符前,便于用户查看上下文或继续向前查找。
作用:
避免重复选中同一位置(如连续点击“查找下一个”时跳过已匹配内容)。
支持连续查找时从匹配内容末尾继续向后搜索。*/
gpos = gpos + lenStrFind; // 移动到匹配内容末尾
}
}
else // 向上查找
{
TRACE(_T("UpWard \n"));
TRACE(_T("gpos 55 = %d. \n"), gpos);
if (gpos == 0)
{
MessageBox(_T("已经向上查找到文件开头,但没找到"), _T("查找替换"),
MB_OK | MB_ICONINFORMATION);
gpos = strEdit.GetLength() - 1; //reset gpos for loop searching.
goto end;
}
/*功能:通过 CEdit::GetBuffer 获取编辑控件的内部文本缓冲区。
参数 0:表示获取整个缓冲区的指针(无需预分配长度)。
返回值:wstring 类型的字符串对象,包含编辑框的完整内容。*/
wstring str = strEdit.GetBuffer(0); // 获取编辑控件的完整文本内容
TRACE(_T("str 55 = %s. \n"), str);
/*功能:在字符串 str 中从位置 gpos 开始反向搜索子字符串 strFind 的最后一次出现。
find_last_of 的特性:
返回 strFind 中任意字符在 str 中最后一次出现的位置。
若未找到,返回 wstring::npos。*/
gpos = str.find_last_of(strFind, gpos); // 从当前位置反向搜索子字符串
strEdit.ReleaseBuffer(); // 释放缓冲区,同步修改到控件
TRACE(_T("gpos 66 = %d. \n"), gpos);
if (gpos == -1)
{
gpos = 0;
MessageBox(_T("无法找到:") + strRawFind);
}
else
{
/*功能:将输入焦点设置到编辑控件 m_edt 上。
作用:
用户可直接在控件中输入或编辑文本。
视觉上控件边框高亮,提示当前活动对象。*/
m_edt.SetFocus();
/*设置选中文本范围
功能:选中编辑控件中从 gpos 开始、长度为 lenStrFind 的文本。
参数说明:
gpos:选中起始位置(通常为匹配子字符串的起始索引)。
gpos + lenStrFind:选中结束位置(起始位置 + 子字符串长度)。*/
m_edt.SetSel(gpos, gpos + lenStrFind);
/*记录当前匹配位置
功能:将当前匹配起始位置 gpos 保存到全局变量 gcurpos。
作用:
为后续替换操作提供基准位置(如替换时需知道选中文本的起始点)。
避免因光标移动或界面刷新导致位置丢失。*/
gcurpos = gpos;
/*调整光标位置
功能:将 gpos 更新为匹配内容起始位置的前一个位置。
逻辑分析:
若原 gpos 为匹配子字符串的起始索引(如 5),更新后 gpos = 5 - lenStrFind。
示例:
若匹配子字符串长度为 3,则 gpos 更新为 2。
光标将停留在匹配内容的起始字符前,便于用户查看上下文或继续向前查找。*/
gpos = gpos - lenStrFind;
}
}
}
// 替换当前匹配项
if (gpFindReplaceDlg->ReplaceCurrent())
{
//检查 gcurpos(匹配起始位置)是否有效(非负)。
if (gcurpos >= 0)
{
/*设置控件焦点
功能:将输入焦点设置到编辑控件 m_edt 上。
作用:
用户可直接在控件中查看替换结果。
确保后续选中操作生效(焦点必须在控件上)。*/
m_edt.SetFocus();
/*选中匹配文本
功能:选中编辑控件中从 gcurpos 开始、长度为 lenStrFind 的文本。
参数说明:
gcurpos:匹配子字符串的起始索引。
gcurpos + lenStrFind:匹配子字符串的结束索引(不含)。
作用:
高亮显示待替换的匹配内容。
为替换操作提供选中范围。*/
m_edt.SetSel(gcurpos, gcurpos + lenStrFind);
TRACE(_T("选中范围:%d - %d\n"), gcurpos, gcurpos + lenStrFind);
/*执行替换操作
功能:用 strReplace 替换当前选中的文本。
特性:
直接操作编辑控件的文本内容,无需手动处理 CString 对象。
替换后,控件内容自动更新,光标移动到替换文本末尾。*/
m_edt.ReplaceSel(strReplace); // 替换选中文本
/*重新选中替换后的文本
功能:选中替换后的新文本(从 gcurpos 到 gcurpos + lenStrReplace)。
逻辑分析:
若 strReplace 长度与 lenStrFind 不同,选中范围会自动调整。
示例:
原匹配长度 lenStrFind = 3,替换后 lenStrReplace = 5。
新选中范围为 gcurpos 到 gcurpos + 5,覆盖整个替换内容。
作用:
高亮显示替换后的新内容,便于用户确认。
为后续操作(如继续查找)提供基准位置。*/
TRACE(_T("lenStrReplace 2 = %d.\n"), lenStrReplace);
TRACE(_T("选中范围:%d - %d\n"), gcurpos, gcurpos + lenStrFind);
m_edt.SetSel(gcurpos, gcurpos + lenStrReplace);
/*更新光标位置
功能:将 gpos 更新为替换后的新位置(起始位置 + 替换后长度)。
作用:
记录替换后的光标位置,支持连续查找或替换。
避免重复处理已替换内容。*/
gpos = gcurpos + lenStrReplace; // 更新光标位置
}
}
// 全部替换
if (gpFindReplaceDlg->ReplaceAll())
{
UpdateData(TRUE);
m_strEdit.Replace(strFind, strReplace); // 直接替换所有匹配项
UpdateData(FALSE);
}
end:
return 0;
}
相关内容
未定义标识符 “wstring“-CSDN博客https://blog.csdn.net/qq_20725221/article/details/147296337?spm=1001.2014.3001.5501ON_REGISTERED_MESSAGE(WM_FINDREPLACE, OnFindReplace) 报“类型转换无效”错误-CSDN博客
https://blog.csdn.net/qq_20725221/article/details/147296987?spm=1001.2014.3001.5501
MFC注册系统预定义消息-CSDN博客https://blog.csdn.net/qq_20725221/article/details/147304289?spm=1001.2014.3001.5501注册用户自定义消息-CSDN博客
https://blog.csdn.net/qq_20725221/article/details/147304299?spm=1001.2014.3001.5501注册系统预定义消息和注册用户自定义消息的差异-CSDN博客
https://blog.csdn.net/qq_20725221/article/details/147304319?spm=1001.2014.3001.5501ON_REGISTERED_MESSAGE和ON_MESSAGE注册消息的差异-CSDN博客
https://blog.csdn.net/qq_20725221/article/details/147304987?spm=1001.2014.3001.5501详细介绍str.Find()和str.find_last_of()-CSDN博客
https://blog.csdn.net/qq_20725221/article/details/147308535?spm=1001.2014.3001.5501strEdit1.Find(strFind1, gpos)得到的返回值与预期不一样-CSDN博客
https://blog.csdn.net/qq_20725221/article/details/147322912?spm=1001.2014.3001.5501CString中为什么会有“零宽空格”?-CSDN博客
https://blog.csdn.net/qq_20725221/article/details/147324010?spm=1001.2014.3001.5501