在 MFC 中使用正则表达式






4.81/5 (26投票s)
2006年3月6日
4分钟阅读

197650

5591
CATLRegExp - 一个 Visual C++ 内置的正则表达式。
引言
我想大多数人在 C++ 项目中使用正则表达式时,会想到 Boost::Regex 或 PCRE。然而,事实上,微软在 ATL 服务器组件中也有自己的正则表达式实现,它叫做 CAtlRegExp
。作为奖励,CAtlRegExp
不仅支持 ASCII 和 Unicode,还支持 MBCS。
支持的正则表达式语法
下表来自 MSDN。您会注意到,该语法与 Perl 的不完全相同。例如,分组运算符是 {},而在 Perl 中是 (),并且它没有 Perl 语法中的 {n}(匹配正好 n 次)<。
元字符 | 含义 |
. | 匹配任何单个字符。 |
[ ] | 表示一个字符类。匹配方括号内的任何字符(例如,[abc] 匹配“a”、“b”和“c”)。 |
^ | 如果此元字符出现在字符类的开头,则它会否定该字符类。否定字符类匹配方括号内的字符以外的任何字符(例如,[^abc] 匹配除“a”、“b”和“c”之外的所有字符)。如果 |
- | 在字符类中,表示字符范围(例如,[0-9] 匹配“0”到“9”之间的任何数字)。 |
? | 表示前面的表达式是可选的:它最多匹配一次(例如,[0-9][0-9]? 匹配“2”和“12”)。 |
+ | 表示前面的表达式匹配一次或多次(例如,[0-9]+ 匹配“1”、“13”、“666”等)。 |
* | 表示前面的表达式匹配零次或多次。 |
??, +?, *? | 非贪婪版本的 ? 、+ 和 * 。这些会尽可能少地匹配,与贪婪版本(尽可能多地匹配)相反。例如:给定输入“<abc><def>”,<.*?> 匹配“<abc>”,而 <.*> 匹配“<abc><def>”。 |
( ) | 分组运算符。例如:(\d+,)*\d+ 匹配由逗号分隔的数字列表(例如,“1”或“1,23,456”)。 |
{ } | 表示一个匹配组。可以通过 CAtlREMatchContext 对象检索输入中匹配大括号内表达式的实际文本。 |
\ | 转义字符:将下一个字符按字面意思解释(例如,[0-9]+ 匹配一个或多个数字,但 [0-9]\+ 匹配一个数字后跟一个加号字符)。也用于缩写(例如,\a 表示任何字母数字字符;请参见下表)。如果 请注意,在 C++ 字符串字面量中,必须使用两个反斜杠: |
$ | 在正则表达式的末尾,此字符匹配输入的末尾。例如:[0-9]$ 匹配输入末尾的数字。 |
| | 选择运算符:分隔两个表达式,其中只有一个匹配(例如,T|the 匹配“The”或“the”)。 |
! | 否定运算符:! 后面的表达式不匹配输入。例如:a!b 匹配后面不是“b”的“a”。 |
CAtlRegExp
可以处理缩写,例如将 [0-9]
替换为 \d
。缩写由传递给 CharTraits
参数的字符特征类提供。预定义的字符特征类提供以下缩写
缩写 | 匹配 |
---|---|
\a | 任何字母数字字符:([a-zA-Z0-9]) |
\b | 空白字符(空格):([ \\t]) |
\c | 任何字母字符:([a-zA-Z]) |
\d | 任何十进制数字:([0-9]) |
\h | 任何十六进制数字:([0-9a-fA-F]) |
\n | 换行符:(\r|(\r?\n)) |
\q | 带引号的字符串:“(\"[^\"]*\")|(\'[^\']*\')” |
\w | 一个简单的单词:([a-zA-Z]+) |
\z | 一个整数:([0-9]+) |
使用代码
虽然 CAtlRegExp
是 ATL 服务器类的一部分,但您不必使用 ATL 项目即可使用此类,只需 #include "atlrx.h"
即可。
我编写了一个简单的基于对话框的程序来测试/演示 CAtlRegExp
。程序的核心代码如下所示。
// create regular expression content CAtlRegExp<> regex; REParseError status = regex.Parse(m_szRegex, m_bCaseSensitive); if (REPARSE_ERROR_OK != status) { // invalid pattern, show error m_szStatus = TEXT("Parser Error: "); m_szStatus += REError2String(status); } else { // valid regex pattern, now try to match the content CAtlREMatchContext<> mc; if (!regex.Match(m_szInput, &mc)) { // content not match m_szStatus = TEXT("No match"); } else { // content match, show match-group m_szStatus = TEXT("Success match"); for (UINT nGroupIndex = 0; nGroupIndex < mc.m_uNumGroups; ++nGroupIndex) { const CAtlREMatchContext<>::RECHAR* szStart = 0; const CAtlREMatchContext<>::RECHAR* szEnd = 0; mc.GetMatch(nGroupIndex, &szStart, &szEnd); ptrdiff_t nLength = szEnd - szStart; CString text(szStart, nLength); m_ctrlListBox.AddString(text); } } }
REError2String
函数如下所示。
// refer to REParseError for more information CString CMfcRegexDlg::REError2String(REParseError status) { switch (status) { case REPARSE_ERROR_OK: return TEXT("No error occurred"); case REPARSE_ERROR_OUTOFMEMORY: return TEXT("Out of memory"); case REPARSE_ERROR_BRACE_EXPECTED: return TEXT("A closing brace was expected"); case REPARSE_ERROR_PAREN_EXPECTED: return TEXT("A closing parenthesis was expected"); case REPARSE_ERROR_BRACKET_EXPECTED: return TEXT("A closing bracket was expected"); case REPARSE_ERROR_UNEXPECTED: return TEXT("An unspecified fatal error occurred"); case REPARSE_ERROR_EMPTY_RANGE: return TEXT("A range expression was empty"); case REPARSE_ERROR_INVALID_GROUP: return TEXT("A back reference was made to a group" " that did not exist"); case REPARSE_ERROR_INVALID_RANGE: return TEXT("An invalid range was specified"); case REPARSE_ERROR_EMPTY_REPEATOP: return TEXT("A repeat operator (* or +) was applied" " to an expression that could be empty"); case REPARSE_ERROR_INVALID_INPUT: return TEXT("The input string was invalid"); default: return TEXT("Unknown error"); } }
关于 MBCS 的特别说明
默认情况下,CAtlRegExp
使用 CAtlRECharTraits
,对于非 Unicode 版本,它是 CAtlRECharTraitsA
。但是,除非您使用严格纯粹的 ASCII,否则应使用 CAtlRECharTraitsMB
;否则,在非 ASCII 文本中可能会遇到一些意外的结果。例如,中文的“词”("word")在 Big5 编码中是双字节词“\0xA6 r”,其第二个字节是“r”。
参考文献
历史
- 2006 年 3 月 6 日:上传初始版本。