65.9K
CodeProject 正在变化。 阅读更多。
Home

在 MFC 中使用正则表达式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (26投票s)

2006年3月6日

4分钟阅读

viewsIcon

197650

downloadIcon

5591

CATLRegExp - 一个 Visual C++ 内置的正则表达式。

Sample image

引言

我想大多数人在 C++ 项目中使用正则表达式时,会想到 Boost::RegexPCRE。然而,事实上,微软在 ATL 服务器组件中也有自己的正则表达式实现,它叫做 CAtlRegExp。作为奖励,CAtlRegExp 不仅支持 ASCII 和 Unicode,还支持 MBCS。

支持的正则表达式语法

下表来自 MSDN。您会注意到,该语法与 Perl 的不完全相同。例如,分组运算符是 {},而在 Perl 中是 (),并且它没有 Perl 语法中的 {n}(匹配正好 n 次)<。

元字符含义
. 匹配任何单个字符。
[ ] 表示一个字符类。匹配方括号内的任何字符(例如,[abc] 匹配“a”、“b”和“c”)。
^ 如果此元字符出现在字符类的开头,则它会否定该字符类。否定字符类匹配方括号内的字符以外的任何字符(例如,[^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 表示任何字母数字字符;请参见下表)。

如果 \ 后面跟着一个数字 n,则它匹配第 n 个匹配组(从 0 开始)。例如:<{.*?}>.*?</\0> 匹配“<head>Contents</head>”。

请注意,在 C++ 字符串字面量中,必须使用两个反斜杠:"\\+""\\a""<{.*?}>.*?</\\0>"

$ 在正则表达式的末尾,此字符匹配输入的末尾。例如:[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 日:上传初始版本。
© . All rights reserved.