MFC中用正则表达式进行有效性验证
转载自:http://blog.csdn.net/jinhill/article/details/5928993
正则表达式最实用的一个地方是验证用户输入。它可以轻松验证邮编、电话号码、信用卡号码——以及现实世界中各种类型的信息。一个正则表达式可以替换成打甚至上百行过程代码。UNIX 和 Web 编程语言如 Perl从一开始就有正则表达式,但在 Windows 世界或MFC,从来都是使用第三方库,一直到 .NET 框架才结束这个局面。因此现在 .NET 提供一个完整的正则表达式库,为什么不在MFC应用程序中使用它呢?利用 RegexWrap 库,你甚至都不需要托管扩展或 /clr。
MFC 已经具备一种称为“对话框数据交换”(Dialog Data Exchange,即 DDX)以及“对话框数据验证”(Dialog Data Validation,即 DDV)的机制来验证对话框输入。从技术上讲,DDX 只是在屏幕和你的对话框对象之间传输数据,而 DDV 才验证数据。当你从对话框的 OnOK 处理例程中调用 UpdateData 时 DDX 才开始工作。
| // user pressed OK: void CMyDialog::OnOK() { UpdateData(TRUE); // 获得对话框数据 ... } |
UpdateData 是一个虚拟 CWnd 函数,你可以在自己的对话框中重写这个函数。其布尔型(Boolean)参数告知是将信息拷贝到屏幕还是相反从屏幕拷贝信息。(你可以在 OnInitDialog 中调用 UpdateData(FALSE)以便初始化对话框)。默认的 CWnd 实现创建一个 CDataExchange 对象并将它传递到另一个虚拟函数,DoDataExchange,你得重写这个函数去调用专门的 DDX 函数来为单独的数据成员传递数据:
| void CMyDialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_NAME, m_name); DDX_Text(pDX, IDC_AGE, m_age); ... // etc. } |
这里 IDC_NAME 和 IDC_AGE 是编辑控制的 IDs,m_name 和 m_age 分别是 CString 和 int 数据成员。DDX_Text 将用户输入的 Name 和 Age 拷贝到 m_name 和 m_age(用一个重载顺便将 Age 转变成 int)。DDX 函数知道走哪条路,因为当从屏幕拷贝到对话框时,CDataExchange::m_bSaveAndValidate 为 TRUE,反之则为 FALSE。MFC 为各种数据和控制类型加载 DDX 函数。例如,DDX_Text 至少有一些重载函数用来将输入文本拷贝和转换成不同的类型,如
CString、int、double、COleCurrency 等等。DDX_Check 用来将复选框的状态转换成整型值,DDX_Radio 则对单选按钮做同样的事情。
DDX 函数传输数据;DDV 函数则验证它。例如,为了限制用户名称为 35个字符,你可以这样做:
| // in CMyDialog::DoDataExchange DDX_Text(pDX, IDC_NAME, m_sName); // 获得/设置值 DDV_MaxChars(pDX, m_sName, 35); // 验证 |
为了限定你的用户年龄为 1-120之间的一个整数,你可以这样写:
| // m_age is int DDX_Text(pDX, IDC_AGE, m_age); DDV_MinMaxInt(pDX, m_age, 1, 120); |
虽然 DDX 工作表现得很好,DDV 是不免有点老土。MFC 在有效性验证方面所能做到的很有限。你可以在文本域中限制数字字符,不同类型的最小/最大约束。最小/最大是不错,但如果你想验证邮编或电话号码怎么办?MFC 对此无能为力。你不得不编写自己的 DDV 函数。当我第一次用正则表达式实现有效性验证时,我只要写一个函数即可,就像这样:
| void DDV_Regex(CDataExchange* pDX, CString& val, LPCTSTR pszRegex) { if (pDX->m_bSaveAndValidate) { CMRegex r(pszRegex); if (!r.Match(val).Success()) { pDX->Fail(); // throws exception } } } |
这使你很容易象下面这样用正则表达式验证输入:
| // in CMyDialog::DoDataExchange DDX_Text(pDX, IDC_ZIP, m_zip); DDV_Regex(pDX, m_zip,_T("^d(-d)?$")); |
好酷啊,仅用四行代码就搞掂。(当然,那要假设你有 RegexWrap——否则你得使用托管扩展直接调用框架 Regex 类。)DDV_Regex 在 MFC 的 DDX/DDV 方案中工作表现很完美,但是当我开始添加更多的域时,我马上发现一些 DDX/DDV 的主要缺点,其一,如果域输入无效,则每个 DDV 函数都显示一个出错消息框并丢出异常,那么要是有五个无效域,用户就会看到五个消息框——真实糟透了!此外,在对 DDV 的调用中,我不想将正则表达式写死在代码中。但我拒绝 DDX/DDV 的主要理由是它太程序化。为了验证新的域,你不得不添加另外的数据成员以及在
DoDataExchange 加更多的代码,不久这个函数便膨胀臃肿,就像下面这样:
| DDX_Text(pDX, IDC_FOO,...); DDV_Mumble(pDX, ...) DDX_Text(pDX, IDC_BAR,...); DDV_Bletch(...) ... // etc for 14 lines |
为什么我非得要墨守成规编写过程指令来描述固有的验证规则呢?我的五条编程最高准则之一是:拒斥程序化代码。另一个是:一个表格胜过一千行代码。你肯定猜到我要干什么了。最终,我编写自己的对话框验证系统,一个基于规则的、表格驱动的验证系统。它依靠在 DDX 的最上层,但废掉了 DDV 并有好得多的用户接口。当然,它还易于使用,借助正则表达式进行验证。所有细节都封装在一个类中,CRegexForm,你可以在任何 MFC 对话框中使用这个类。
![]() Figure 1 TestForm 里的工具提示 与往常一样,我编写了一个测试程序来示范它的工作原理。初看起来,TestForm 有点像再平常不过的基于 MFC 的对话框程序。它的主对话框中有几个编辑框——邮编、SSN(社会保险号),电话号码等。但当你通过对 TestForm 的测试,你会很快认识到它蕴含着许多玄机。如果你用tab键在输入域间移动,TestForm 会显示一个工具提示,它描述在这个输入域能输入什么(如图 Figure 1)。如果你敲入了一个非法字符——例如,在电话号码输入域敲入一个字符——TestForm 将拒绝接受该字符并发出蜂鸣声。当你按下确认键或OK键,你会得到一个描述所有无效输入域的出错信息框,如图
所有这些神奇的特性都是由 CRegexForm 自己实现的。你只需使用它即可,使用方法相当直白,首先你得定义一个自己窗体。下面是 TestForm 的窗体内容,这些定义位于 MainDlg.cpp:
这个宏定义了一个静态表格,表格描述每个编辑控制域。大多数情况下你只需要控制 ID,还要有地方放标志和 RegexOptions。例如,在 TestForm 中,邮政编码是必输域(RGXF_REQUIRED),质数(Prime Number)输入域使用回调(稍后会详细讨论),最喜爱的专栏作家(IDC_FAVCOL)指定 CMRegex::IgnoreCase,它使得大小写 不敏感。
看着表格你可能想知道正则表达式在哪。回答是:在资源文件中。对于每个域/控制ID,CRegexForm 都期望有一个具有相同ID的资源串。资源串由五个子串组成,子串之间用新行符(''n'')分隔。一般格式为:
第一个子串“Zip Code”是域名。第二个“^d(-d)?$”,是用于验证邮编的正则表达式。(在资源串中必须敲入两个反斜线,目的是转义正则表达式中反斜线)。第三个子串是另外一个正则表达式,用来描述合法字符。对于邮编来说,即是“[d-]”,意思是允许数字和连字符(hyphen)。如果输入域无字符限制,你可以通过敲入两个连续的新行符(“nn”意思是空子串)省略 LegalChars 检查。第四个子串全部为工具提示串。最后你可以提供第五个子串,如果该输入域无效则显示错误信息。对于邮编来说,它没有错误信息,所以
现在 substrs[i] 是第 i 个子串,如果你想知道有多少个子串,调用 substrs.size()即可。我真的很高兴我包装了 Split 函数来返回 STL vector。
自然,CRegexForm 需要域映射并指向你的对话框;第二和第三个参数是另一个资源串和回调消息ID。与单独的域字符串一样,初始串由包含新行符分隔的多个子串组成。对于 TestForm,IDS_MYREGEXFORM 是“Error: %snRequirednShould be: %snBad Value”。第一个子串“Error: %s”是错误前缀。CRegexForm 用来显示“Error: xxx,”,xxx 是实际的出错信息。第二个子串,“Required,”是一个词/短语,当该域为必输域(RGXF_REQUIRED)时使用。第三个子串“Should Init 的最后一个参数 MYWM_RGXFORM_MESSAGE 是应用程序定义的回调消息 ID,利用它可以使 CRegexForm 与你的应用程序沟通并做一些需要过程代码来定制验证的事情。如果你需要用数学算法来验证你的输入,你可以在域标志中设置 RGXF_CALLBACK,CRegexForm 将在进行验证时用通知代码 RGXNM_VALIDATEFIELD 方式向对话框发送回调消息。TestForm 使用回调来验证其 Prime Number 域;具体详细参见 Figure 4。
CRegexForm 用其内部拥有的 CStrings 来进行 DDX,所以你不必为每个文本域定义对话框成员。你只要调用 CRegexForm 来传递数据即可。
当你初始化 CRegexForm 时,它分配一个 protected 类型的 FLDINFO 结构数组,映射中的每个域都有一个这样的数组。FLDINFO 结构的成员之一是 FLDINFO::val,类型为 CString,用来保存当前的域值。CRegexForm 在内部使用以此 CString 作为参数的 DDX_Text。你可以通过调用 CRegexForm::GetFieldValue 或 SetFieldValue 获取或设置该内部域值,它们都用控制ID来区分域。
CRegexForm 将所有值都当作文本对待,并将它们存储在 CStrings 中,同时提供 GetFieldValInt 和 GetFieldValDouble 方法来获得转换为 int 或 double 的值。对于其它类型,你得自己进行转换——或者你仍可以用 MFC DoDataExchange 中的 DDX 函数。TestForm 有一个 “Populate”按钮,它调用 CRegexForm::SetFieldValue 将样板数据填充到窗体中,如图 Figure 3 所示。通常,CRegexForm
UpdateData 调用 MFC 的 DDX 机制,即调用对话框的 DoDataExchange。然后 DoDataExchange 调用 CRegexForm::DoDataExchange,从而将用户输入拷贝到其内部的 FLDINFO 结构。接着 CRegexForm::Validate 遍历输入域,针对域的正则表达式调用 CMRegex::Match 来验证每一个域。如果域输入无效,CRegexForm 便在其内部的 FLDINFO 中设置错误码 RGXERR_NOMATCH 或者必输于域如果为空,则设置
我提到的反馈窗口,它由 CRegexForm 全权管理;你只需通过调用 CRegexForm::SetFeedBackWindow 提供一个这样的窗口即可。你可以为出错信息指定一种颜色。CRegexForm 还负责提示功能。默认情况下,当用户tab到某个新输入域时显示域提示(如图 Figure 1)。你可以调用 CRegexForm::SetShowHints(FALSE)来关闭提示功能。SetShowHints(TRUE, nDelay, nTimeout) 可以再次打开提示功能,这里 nDelay 是显示提示之前要等待的毫秒数(默认值=250),nTimeout
在实际应用中,你会将这些值拷贝到其最终目的地。Figure 6 是 CMainDlg::OnOK 的全部代码。通过数据交换中的非耦合式的数据验证,CRegexForm 给予你更充分的 UI 控制,并使你避免 MFC 固有的缺陷。
我提到的反馈窗口,它由 CRegexForm 全权管理;你只需通过调用 CRegexForm::SetFeedBackWindow 提供一个这样的窗口即可。你可以为出错信息指定一种颜色。CRegexForm 还负责提示功能。默认情况下,当用户tab到某个新输入域时显示域提示(如图 Figure 1)。你可以调用 CRegexForm::SetShowHints(FALSE)来关闭提示功能。SetShowHints(TRUE, nDelay, nTimeout) 可以再次打开提示功能,这里 nDelay 是显示提示之前要等待的毫秒数(默认值=250),nTimeout |
MFC中用正则表达式进行有效性验证的更多相关文章
- js 常用正则表达式表单验证代码
正则表达式使用详解 简介 简单的说,正则表达式是一种可以用于模式匹配和替换的强有力的工具.其作用如下:测试字符串的某个模式.例如,可以对一个输入字符串进行测试,看在该字符串是否存在一个电话号码模式或一 ...
- js身份证号有效性验证
1.简述 最近做的系统有用到实名验证的,起初对于用户身份证号只是简单地使用正则表达式进行验证, 很多无效的身份证号就成了漏网之鱼. 导致后台存表里很多无效的身份证号,随便输入用户名和身份证号就可以实名 ...
- ABP(现代ASP.NET样板开发框架)系列之17、ABP应用层——参数有效性验证
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之17.ABP应用层——参数有效性验证 ABP是“ASP.NET Boilerplate Project (ASP. ...
- 密码有效性验证失败。该密码不够复杂,不符合 Windows 策略要求
我在sqlserver2005中建立一个用户的时候,我的用户名和密码是一样的,它不允许我建立报“密码有效性验证失败.该密码不够复杂,不符合 Windows 策略要求”错误,我把密码改成其他一些就可以, ...
- 如何使用JavaScript和正则表达式进行数据验证
利用客户端JavaScript的优势,JavaScript中的正则表达式可以简化数据验证的工作,下面与大家分享下如何使用JavaScript和正则表达式进行数据验证,感兴趣的朋友可以参考下哈 数据验证 ...
- ABP应用层——参数有效性验证
ABP应用层——参数有效性验证 基于DDD的现代ASP.NET开发框架--ABP系列之17.ABP应用层——参数有效性验证 ABP是“ASP.NET Boilerplate Project (ASP. ...
- Java中用正则表达式判断日期格式是否正确
1.Java中用正则表达式判断日期格式是否正确 DateType.java: /** * @Title:DateType.java * @Package:com.you.dao * @Descript ...
- Java中用正则表达式找出数字
Java中用正则表达式找出数字 1.题目 String str = "fjd789klsd908434jk#$$%%^38488545",从中找出78990843438488 ...
- js常用正则表达式表单验证代码
方法一: var re=/正则表达式/; re.test($("txtid").val()) 方法二: $("txtid").val.match(/正则 ...
随机推荐
- matconv-GPU 编译问题
如出现以下错误: 1 error detected in the compilation of "C:/Users/Justin/AppData/Local/Temp/tmpxft_0000 ...
- Chameleon-mini简介
ChameleonMini(变色龙)原德国大学在研究RFID安全时所设计的一块针对多频段多类型RFID模拟的硬件,其设计本身支持ISO14443和ISO15693标准协议,最简单直接的用法就是把获取到 ...
- OpenCV学习5-----使用Mat合并多张图像
最近做实验需要对比实验结果,需要将几张图片拼在一起,直观对比. 尝试用OpenCV解决. 核心思想其实是 声明一个足够大的,正好容纳下那几张图片的mat,然后将拼图依次copy到大图片相应的位置. ...
- 阿里校招内推C++岗位编程题第一题 空格最少的字符串
给定一个字符串S和有效单词的字典D,请确定可以插入到S中的最小空格数,使得最终的字符串完全由D中的有效单词组成.并输出解. 如果没有解则应该输出n/a 例如: 输入: S = “ilikealibab ...
- selenium中的三种等待方式(显示等待WebDriverWait()、隐式等待implicitly()、强制等待sleep())---基于python
我们在实际使用selenium或者appium时,等待下个等待定位的元素出现,特别是web端加载的过程,都需要用到等待,而等待方式的设置是保证脚本稳定有效运行的一个非常重要的手段,在selenium中 ...
- nodejs笔记--express篇(五)
创建一个express + ejs的项目 express -e testEjsWebApp cd testEjsWebApp npm install http://localhost:3000 Usa ...
- js经典试题之数据类型
js经典试题之数据类型 1:输出"B" + "a" + + "B" + "a"的值: 答案:BaNaNa. 分析:因为+ ...
- 硬件PCB Layout布局布线Checklist检查表(通用版)
按部位分类 技术规范内容 1 PCB布线与布局 PCB布线与布局隔离准则:强弱电流隔离.大小电压隔离,高低频率隔离.输入输出隔离.数字模拟隔离.输入输出隔离,分界标准为相差一个数量级.隔离方法包括:空 ...
- php添加扩展 在phpinfo能看到该扩展,但在cli用php -m 却看不到,为什么呢,求指教
1. 没有出现的原因是:执行时添加上php.ini的文件就可以了 $ /usr/local/php/bin/php -c /usr/local/php/etc/php.ini -m | grep ...
- Shell脚本查看linux系统性能瓶颈
脚本目的:分析系统资源性能瓶颈 脚本功能: 1.查看CPU利用率与负载(top.vmstat.sar) 2.查看磁盘.Inode利用率与I/O负载(df.iostat.iotop.sar.dstat) ...



