详细分析“查找/替换对话框的使用”的代码

书籍:《Visual C++ 2017从入门到精通》的3.8.5 查找/替换对话框的使用

环境:visual studio 2022

内容:[例 3.14] 查找/替换对话框的使用

说明:以上内容大部分来自腾讯元宝。

1.新建对话框工程

一个简单的对话框程序-CSDN博客https://blog.csdn.net/qq_20725221/article/details/146396703?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522d04c513b850b06d55102b36c83a4515f%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=d04c513b850b06d55102b36c83a4515f&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-146396703-null-null.nonecase&utm_term=%E5%AF%B9%E8%AF%9D%E6%A1%86&spm=1018.2226.3001.44502.添加编辑框

资源视图->对话框->删除原有控件->在工具箱中找到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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值